@guanlin4924/devflow-pi 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/plugins/devflow-core/extensions/devflow-core.ts +297 -14
  4. package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +3 -2
  5. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_assessment.rb +458 -0
  6. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_brief.rb +256 -0
  7. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_constraints.rb +155 -0
  8. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_feasibility.rb +176 -0
  9. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +46 -0
  10. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +1 -0
  11. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/machine_evidence.rb +1 -0
  12. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +187 -12
  13. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/runtime_context_collector.rb +210 -0
  14. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/stale_check.rb +56 -0
  15. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/static_runtime_context_collector.rb +518 -0
  16. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +83 -1
  17. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/workload_constraint.rb +57 -0
  18. package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +54 -2
  19. package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +3 -2
  20. package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +17 -1
  21. package/plugins/devflow-design/skills/workflow-architecture-feasibility/SKILL.md +37 -0
  22. package/plugins/devflow-design/skills/workflow-architecture-feasibility/agents/openai.yaml +4 -0
  23. package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +7 -2
  24. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +6 -1
package/README.md CHANGED
@@ -34,6 +34,8 @@ devflow-pi
34
34
  /usage-stats
35
35
  ```
36
36
 
37
+ `/devflow project-init` 会在边界确认时提供两种模式:`plan`(默认,只读规划)和 `apply`(写入初始化)。选择 `apply` 并确认后,只会创建 Devflow 的 `.planning/` 与 `.context/` 初始化产物,不会修改业务源码、依赖或项目配置。
38
+
37
39
  用量统计默认只读取当前项目的隔离会话:`/usage-stats`。需要明确查看历史 OMP 会话时,使用 `/usage-stats omp`,或加筛选条件:`/usage-stats omp --from 2026-08-01 --to 2026-08-10`。历史统计保留真实会话路径,不再把不同项目的长会话文件合并成 `main`;统计只使用 assistant message 的原始 usage,不虚构工具调用成本分摊。
38
40
 
39
41
  会话继续使用 `devflow-pi --resume`;也可用 `devflow-pi --resume <session-id>` 打开当前项目的指定 Session。OMP 17.2.7 退出时仍会显示上游硬编码的 `omp --resume` 提示,在专项发行版中应替换为这里的 `devflow-pi --resume`,不会去读取官方 OMP 的 Session 目录。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanlin4924/devflow-pi",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "description": "Devflow 专项研发 Pi,基于固定版本 OMP Runtime 组装研发状态机、代码智能、设计、实现与质量门禁。",
6
6
  "license": "UNLICENSED",
@@ -16,6 +16,13 @@ export type SessionControlState = {
16
16
  label: string;
17
17
  };
18
18
 
19
+ type ProjectInitializationMode = "plan" | "apply";
20
+
21
+ type ProjectInitializationRequest = {
22
+ summary: string;
23
+ mode: ProjectInitializationMode;
24
+ };
25
+
19
26
  type ActiveExecutionContract = {
20
27
  contractId: string;
21
28
  feature: string;
@@ -91,6 +98,13 @@ export function planningState(intent: "project-init" | "requirement-draft"): Ses
91
98
  };
92
99
  }
93
100
 
101
+ function parseProjectInitializationMode(value: string): ProjectInitializationMode | null {
102
+ const normalized = value.trim().toLowerCase();
103
+ if (["", "plan", "readonly", "read-only", "只读", "规划"].includes(normalized)) return "plan";
104
+ if (["apply", "write", "写入", "执行"].includes(normalized)) return "apply";
105
+ return null;
106
+ }
107
+
94
108
  export function executingState(label = "当前工作流步骤执行"): SessionControlState {
95
109
  return { intent: "workflow-execute", phase: "executing", label };
96
110
  }
@@ -731,7 +745,7 @@ export default function devflowCore(pi: ExtensionAPI) {
731
745
  confirm(title: string, message: string): Promise<boolean>;
732
746
  notify(message: string, type?: "info" | "warning" | "error"): void;
733
747
  };
734
- }): Promise<string | null> {
748
+ }): Promise<ProjectInitializationRequest | null> {
735
749
  const projectName = await ctx.ui.input("Devflow 项目初始化", "项目名称(留空则使用当前目录)");
736
750
  const frontendPath = await ctx.ui.input("前端边界", "前端目录;没有请填写 none");
737
751
  if (frontendPath === undefined) return null;
@@ -739,18 +753,30 @@ export default function devflowCore(pi: ExtensionAPI) {
739
753
  if (backendPath === undefined) return null;
740
754
  const buildIndex = await ctx.ui.input("代码索引", "建立索引?输入 yes 或 no");
741
755
  if (buildIndex === undefined) return null;
756
+ const initializationMode = await ctx.ui.input("初始化方式", "输入 plan(只读规划,默认)或 apply(写入初始化)");
757
+ if (initializationMode === undefined) return null;
758
+ const mode = parseProjectInitializationMode(initializationMode);
759
+ if (!mode) {
760
+ ctx.ui.notify("初始化方式只能是 plan(只读规划)或 apply(写入初始化);未写入任何文件。", "error");
761
+ return null;
762
+ }
763
+ const modeSummary = mode === "apply" ? "写入初始化" : "只读规划";
742
764
  const summary = [
743
765
  `项目名:${projectName?.trim() || "当前目录"}`,
744
766
  `前端:${frontendPath.trim() || "未提供"}`,
745
767
  `后端:${backendPath.trim() || "未提供"}`,
746
768
  `建立代码索引:${buildIndex.trim() || "未提供"}`,
769
+ `初始化方式:${modeSummary}`,
747
770
  ].join("\n");
748
- const confirmed = await ctx.ui.confirm("确认项目初始化范围", `${summary}\n\n确认后只进入只读规划,不会创建工作流或写入项目文件。`);
771
+ const consequence = mode === "apply"
772
+ ? "确认后会创建 Devflow 的 .planning/ 与 .context/ 初始化产物;不会修改业务源码、依赖或项目配置。"
773
+ : "确认后只进入只读规划,不会创建工作流或写入项目文件。";
774
+ const confirmed = await ctx.ui.confirm("确认项目初始化范围", `${summary}\n\n${consequence}`);
749
775
  if (!confirmed) {
750
776
  ctx.ui.notify("项目初始化已取消;未写入任何文件。", "info");
751
777
  return null;
752
778
  }
753
- return summary;
779
+ return { summary, mode };
754
780
  }
755
781
 
756
782
  async function runRuby(cwd: string, relativeScript: string, args: string[]) {
@@ -952,7 +978,7 @@ export default function devflowCore(pi: ExtensionAPI) {
952
978
  }
953
979
 
954
980
  let activeWorkflowTurn = false;
955
- let pendingProjectInitialization: string | undefined;
981
+ let pendingProjectInitialization: ProjectInitializationRequest | undefined;
956
982
 
957
983
  async function triggerWorkflow(snapshot: Record<string, unknown>, source: "start" | "approval", requirementDraft?: string) {
958
984
  activeWorkflowTurn = true;
@@ -1013,7 +1039,7 @@ export default function devflowCore(pi: ExtensionAPI) {
1013
1039
  }
1014
1040
  setControlState(ctx, executingState("执行已批准的项目初始化"));
1015
1041
  activeWorkflowTurn = true;
1016
- return { text: projectInitializationDirective(pendingProjectInitialization), handled: false };
1042
+ return { text: projectInitializationDirective(pendingProjectInitialization.summary), handled: false };
1017
1043
  }
1018
1044
  if (controlAction === "reject") {
1019
1045
  ctx.ui.notify("已保留初始化规划,未创建任何项目控制文件。请直接说明需要调整的初始化范围。", "info");
@@ -1070,9 +1096,14 @@ export default function devflowCore(pi: ExtensionAPI) {
1070
1096
  return { handled: true };
1071
1097
  }
1072
1098
  if (intent === "project-init") {
1073
- const summary = await collectProjectInitialization(ctx);
1074
- if (!summary) return { handled: true };
1075
- pendingProjectInitialization = summary;
1099
+ const initialization = await collectProjectInitialization(ctx);
1100
+ if (!initialization) return { handled: true };
1101
+ pendingProjectInitialization = initialization;
1102
+ if (initialization.mode === "apply") {
1103
+ setControlState(ctx, executingState("执行已确认的项目初始化"));
1104
+ activeWorkflowTurn = true;
1105
+ return { text: projectInitializationDirective(initialization.summary), handled: false };
1106
+ }
1076
1107
  const state = planningState("project-init");
1077
1108
  setControlState(ctx, state);
1078
1109
  return {
@@ -1081,7 +1112,7 @@ export default function devflowCore(pi: ExtensionAPI) {
1081
1112
  "用户已确认项目初始化的输入。你现在是 Planner,只允许只读分析和派发 project-scout / requirements-auditor / contract-auditor。",
1082
1113
  "不得创建 .planning 文件、不得调用状态迁移、不得执行实现。",
1083
1114
  "先输出:已知事实、待验证点、最少侦察任务和必须由用户决定的问题。没有证据时写 unknown。",
1084
- summary,
1115
+ initialization.summary,
1085
1116
  "</devflow-planner-directive>",
1086
1117
  ].join("\n"),
1087
1118
  };
@@ -1310,7 +1341,7 @@ export default function devflowCore(pi: ExtensionAPI) {
1310
1341
  read: z.boolean().optional(),
1311
1342
  delta: z.object({
1312
1343
  op: z.enum(["add", "link", "update"]),
1313
- kind: z.enum(["source", "code", "term", "object", "behavior", "requirement", "rule", "boundary", "slice", "risk", "decision", "acceptance", "api"]).optional(),
1344
+ kind: z.enum(["source", "code", "term", "object", "behavior", "requirement", "rule", "boundary", "slice", "risk", "decision", "acceptance", "api", "workload_shape", "workload_constraint"]).optional(),
1314
1345
  id: z.string().min(1).optional(),
1315
1346
  data: z.record(z.string(), z.unknown()).optional(),
1316
1347
  from: z.string().min(1).optional(),
@@ -1340,6 +1371,36 @@ export default function devflowCore(pi: ExtensionAPI) {
1340
1371
  return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "confirm requires expectedRevision, nodeId and evidence" });
1341
1372
  }
1342
1373
  args.push("--write", "--expected-revision", String(params.expectedRevision), "--node-id", params.nodeId, "--evidence-json", JSON.stringify(params.evidence));
1374
+ const inspected = parseJsonResult(
1375
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/requirement_graph.rb", ["inspect", "--feature", params.feature, "--json"]),
1376
+ );
1377
+ const node = Array.isArray(inspected?.graph?.nodes)
1378
+ ? inspected.graph.nodes.find((candidate: unknown) => typeof candidate === "object" && candidate !== null && (candidate as { id?: unknown }).id === params.nodeId) as { kind?: unknown; data?: unknown } | undefined
1379
+ : undefined;
1380
+ const architectureDecision = node?.kind === "decision" && typeof node?.data === "object" && node.data !== null && (node.data as { decision_type?: unknown }).decision_type === "architecture_option_comparison";
1381
+ const architectureRisk = node?.kind === "risk" && typeof node?.data === "object" && node.data !== null && (node.data as { risk_type?: unknown }).risk_type === "architecture_option_risk";
1382
+ if (node?.kind === "workload_shape" || node?.kind === "workload_constraint" || architectureDecision || architectureRisk) {
1383
+ const preview = JSON.stringify(node.data ?? {}, null, 2);
1384
+ const decisionDialog = architectureDecision;
1385
+ const riskDialog = architectureRisk;
1386
+ const confirmed = await ctx.ui.confirm(
1387
+ decisionDialog ? "确认架构方案取舍" : riskDialog ? "确认架构风险处理" : "确认工作负载事实或缺口",
1388
+ [
1389
+ `Feature:${params.feature}`,
1390
+ `节点:${params.nodeId}`,
1391
+ `类型:${decisionDialog ? "架构方案比较" : riskDialog ? "架构风险处理" : node.kind === "workload_shape" ? "工作负载形态" : "工作负载约束"}`,
1392
+ decisionDialog
1393
+ ? "确认的含义是:你已核对候选方案、证据与取舍,并接受其中 recommendation_option_id 作为本次架构选择。"
1394
+ : riskDialog
1395
+ ? "确认的含义是:你已核对失败模式、影响、处理方式、负责人和复核条件;它不代表生产风险从此消失。"
1396
+ : "确认的含义仅是:该事实或缺口、来源与责任归属已被你核对;它不代表容量已经满足,也不会替你选择实现方案。",
1397
+ "待确认内容:",
1398
+ preview.length > 8_000 ? `${preview.slice(0, 8_000)}\n…(内容过长,已截断)` : preview,
1399
+ ].join("\n\n"),
1400
+ );
1401
+ if (!confirmed) return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "User declined workload confirmation; node remains candidate." });
1402
+ args.push("--confirmed-interactively");
1403
+ }
1343
1404
  }
1344
1405
  return renderResult(
1345
1406
  await runRuby(ctx.cwd, "workflow-orchestrator/scripts/requirement_graph.rb", args),
@@ -1348,6 +1409,222 @@ export default function devflowCore(pi: ExtensionAPI) {
1348
1409
  },
1349
1410
  });
1350
1411
 
1412
+ pi.registerTool({
1413
+ name: "devflow_runtime_context",
1414
+ label: "Devflow Runtime Context",
1415
+ description: "Collect or inspect the P0 repository-declared dependency snapshot and its coverage.",
1416
+ parameters: z.object({
1417
+ action: z.enum(["collect", "inspect"]),
1418
+ }),
1419
+ approval: (args) => args.action === "collect" ? "write" : "read",
1420
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1421
+ return renderResult(
1422
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/runtime_context_collector.rb", [params.action, "--json"]),
1423
+ { action: params.action },
1424
+ );
1425
+ },
1426
+ });
1427
+
1428
+ pi.registerTool({
1429
+ name: "devflow_static_runtime_context",
1430
+ label: "Devflow Static Runtime Context",
1431
+ description: "Collect or inspect the P1 static evidence snapshot. It records only repository declarations and references, never production health or capacity.",
1432
+ parameters: z.object({
1433
+ action: z.enum(["collect", "inspect"]),
1434
+ }),
1435
+ approval: (args) => args.action === "collect" ? "write" : "read",
1436
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1437
+ return renderResult(
1438
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/static_runtime_context_collector.rb", [params.action, "--json"]),
1439
+ { action: params.action },
1440
+ );
1441
+ },
1442
+ });
1443
+
1444
+ pi.registerTool({
1445
+ name: "devflow_architecture_assessment",
1446
+ label: "Devflow Architecture Assessment",
1447
+ description: "Confirm an immutable delta set through an explicit user dialog, then assess only that Core-owned manifest against a pinned runtime-context snapshot.",
1448
+ parameters: z.object({
1449
+ action: z.enum(["inspect", "assess", "write-manifest"]),
1450
+ feature: z.string().min(1),
1451
+ baseSnapshotRevision: z.number().int().min(0).optional(),
1452
+ deltaSetId: z.string().min(1).optional(),
1453
+ deltaJson: z.array(z.record(z.string(), z.unknown())).optional(),
1454
+ sourceBundleRevision: z.string().min(1).optional(),
1455
+ expectedAssessmentRevision: z.number().int().min(0).optional(),
1456
+ write: z.boolean().optional(),
1457
+ }),
1458
+ approval: (args) => args.action === "write-manifest" || args.write ? "write" : "read",
1459
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1460
+ const args = [params.action, "--feature", params.feature, "--json"];
1461
+ if (params.action === "inspect") {
1462
+ if (params.deltaSetId) args.push("--delta-set-id", params.deltaSetId);
1463
+ return renderResult(
1464
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_assessment.rb", args),
1465
+ { action: params.action, feature: params.feature },
1466
+ );
1467
+ }
1468
+ if (!params.deltaSetId) {
1469
+ return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "deltaSetId is required" });
1470
+ }
1471
+ args.push("--delta-set-id", params.deltaSetId);
1472
+ if (params.action === "assess") {
1473
+ if (params.baseSnapshotRevision === undefined) {
1474
+ return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "assess requires baseSnapshotRevision and deltaSetId; deltas and manifest are Core-owned" });
1475
+ }
1476
+ args.push("--base-snapshot-revision", String(params.baseSnapshotRevision));
1477
+ if (params.write) {
1478
+ const inspected = parseJsonResult(await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_assessment.rb", ["inspect", "--feature", params.feature, "--delta-set-id", params.deltaSetId, "--json"]));
1479
+ const currentRevision = typeof inspected?.current_assessment_revision === "number" ? inspected.current_assessment_revision : undefined;
1480
+ if (currentRevision === undefined) {
1481
+ return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "Unable to read the current assessment revision for CAS." });
1482
+ }
1483
+ args.push("--write", "--expected-assessment-revision", String(currentRevision));
1484
+ }
1485
+ }
1486
+ if (params.action === "write-manifest") {
1487
+ if (!params.sourceBundleRevision || !params.deltaJson?.length) {
1488
+ return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "write-manifest requires deltaSetId, sourceBundleRevision and a non-empty deltaJson" });
1489
+ }
1490
+ const preview = params.deltaJson.map((rawDelta, index) => {
1491
+ const delta = rawDelta as Record<string, unknown>;
1492
+ const source = delta.source && typeof delta.source === "object" ? delta.source as Record<string, unknown> : {};
1493
+ return [
1494
+ `${index + 1}. ID:${String(delta.id ?? "(缺失)")}`,
1495
+ `依赖:${String(delta.subject ?? "(缺失)")}`,
1496
+ `来源:${String(source.locator ?? "(缺失)")}`,
1497
+ `来源 revision:${String(source.repository_revision ?? "(缺失)")}`,
1498
+ `内容哈希:${String(source.input_hash ?? "(缺失)")}`,
1499
+ ].join("\n");
1500
+ }).join("\n\n");
1501
+ const confirmed = await ctx.ui.confirm(
1502
+ "确认架构评估 Delta 集合",
1503
+ [
1504
+ `Feature:${params.feature}`,
1505
+ `Delta 集合:${params.deltaSetId}`,
1506
+ `需求/方案 revision:${params.sourceBundleRevision}`,
1507
+ "确认后 Core 会创建不可覆盖的 manifest;后续评估只能读取这一份确认内容。",
1508
+ "请核对以下依赖、来源与哈希:",
1509
+ preview,
1510
+ ].join("\n\n"),
1511
+ );
1512
+ if (!confirmed) return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "User declined delta-set confirmation; no manifest was written." });
1513
+ args.push("--source-bundle-revision", params.sourceBundleRevision, "--delta-json", JSON.stringify(params.deltaJson), "--confirmed-interactively", "--write");
1514
+ }
1515
+ return renderResult(
1516
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_assessment.rb", args),
1517
+ { action: params.action, feature: params.feature },
1518
+ );
1519
+ },
1520
+ });
1521
+
1522
+ pi.registerTool({
1523
+ name: "devflow_workload_constraint",
1524
+ label: "Devflow Workload Constraint",
1525
+ description: "Validate a workload constraint (NFR) against its source-status schema.",
1526
+ parameters: z.object({
1527
+ constraintJson: z.record(z.string(), z.unknown()),
1528
+ }),
1529
+ approval: "read",
1530
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1531
+ return renderResult(
1532
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/workload_constraint.rb", ["validate", "--constraint-json", JSON.stringify(params.constraintJson), "--json"]),
1533
+ {},
1534
+ );
1535
+ },
1536
+ });
1537
+
1538
+ pi.registerTool({
1539
+ name: "devflow_architecture_constraint",
1540
+ label: "Devflow Architecture Constraint",
1541
+ description: "Validate candidate workload shape or workload constraint inputs without assigning confirmation, decisions, or gate status.",
1542
+ parameters: z.object({
1543
+ action: z.enum(["validate-shape", "validate-constraint"]),
1544
+ shapeJson: z.record(z.string(), z.unknown()).optional(),
1545
+ constraintJson: z.record(z.string(), z.unknown()).optional(),
1546
+ }),
1547
+ approval: "read",
1548
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1549
+ if (params.action === "validate-shape" && !params.shapeJson) {
1550
+ return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "validate-shape requires shapeJson" });
1551
+ }
1552
+ if (params.action === "validate-constraint" && !params.constraintJson) {
1553
+ return renderResult({ ok: false, exitCode: 2, stdout: "", stderr: "validate-constraint requires constraintJson" });
1554
+ }
1555
+ const args = [params.action, "--json"];
1556
+ if (params.shapeJson) args.push("--shape-json", JSON.stringify(params.shapeJson));
1557
+ if (params.constraintJson) args.push("--constraint-json", JSON.stringify(params.constraintJson));
1558
+ return renderResult(
1559
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_constraints.rb", args),
1560
+ { action: params.action },
1561
+ );
1562
+ },
1563
+ });
1564
+
1565
+ pi.registerTool({
1566
+ name: "devflow_architecture_feasibility",
1567
+ label: "Devflow Architecture Feasibility",
1568
+ description: "Read confirmed workload facts and a pinned static-context snapshot to derive an advisory architecture assessment. It never selects an option or claims production capacity.",
1569
+ parameters: z.object({
1570
+ feature: z.string().min(1),
1571
+ }),
1572
+ approval: "read",
1573
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1574
+ return renderResult(
1575
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_feasibility.rb", ["assess", "--feature", params.feature, "--json"]),
1576
+ { feature: params.feature },
1577
+ );
1578
+ },
1579
+ });
1580
+
1581
+ pi.registerTool({
1582
+ name: "devflow_architecture_brief",
1583
+ label: "Devflow Architecture Brief",
1584
+ description: "Inspect or regenerate the Core-derived architecture brief from the current graph and static-context snapshot. The brief is a view, never an agent-authored source of truth.",
1585
+ parameters: z.object({
1586
+ action: z.enum(["inspect", "render"]),
1587
+ feature: z.string().min(1),
1588
+ }),
1589
+ approval: (args) => args.action === "render" ? "write" : "read",
1590
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1591
+ if (params.action === "inspect") {
1592
+ return renderResult(
1593
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_brief.rb", ["inspect", "--feature", params.feature, "--json"]),
1594
+ { action: params.action, feature: params.feature },
1595
+ );
1596
+ }
1597
+ const inspected = parseJsonResult(
1598
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_brief.rb", ["inspect", "--feature", params.feature, "--json"]),
1599
+ );
1600
+ const currentRevision = typeof inspected?.brief_revision === "number" ? inspected.brief_revision : undefined;
1601
+ if (currentRevision === undefined) {
1602
+ return renderResult({ ok: false, exitCode: 1, stdout: "", stderr: "Unable to read the current architecture brief revision for CAS." });
1603
+ }
1604
+ return renderResult(
1605
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/architecture_brief.rb", ["render", "--feature", params.feature, "--write", "--expected-brief-revision", String(currentRevision), "--json"]),
1606
+ { action: params.action, feature: params.feature },
1607
+ );
1608
+ },
1609
+ });
1610
+
1611
+ pi.registerTool({
1612
+ name: "devflow_stale_check",
1613
+ label: "Devflow Stale Check",
1614
+ description: "Check field-level staleness of consumed evidence against the current snapshot.",
1615
+ parameters: z.object({
1616
+ consumedRevision: z.number().int().min(0),
1617
+ evidencePaths: z.array(z.string().min(1)),
1618
+ }),
1619
+ approval: "read",
1620
+ async execute(_id, params, _signal, _onUpdate, ctx) {
1621
+ return renderResult(
1622
+ await runRuby(ctx.cwd, "workflow-orchestrator/scripts/stale_check.rb", ["check", "--consumed-revision", String(params.consumedRevision), "--evidence-paths", JSON.stringify(params.evidencePaths), "--json"]),
1623
+ {},
1624
+ );
1625
+ },
1626
+ });
1627
+
1351
1628
  pi.registerTool({
1352
1629
  name: "devflow_evidence",
1353
1630
  label: "Devflow Evidence Pack",
@@ -1780,9 +2057,15 @@ export default function devflowCore(pi: ExtensionAPI) {
1780
2057
  return;
1781
2058
  }
1782
2059
  if (trimmed === "project-init") {
1783
- const summary = await collectProjectInitialization(ctx);
1784
- if (!summary) return;
1785
- pendingProjectInitialization = summary;
2060
+ const initialization = await collectProjectInitialization(ctx);
2061
+ if (!initialization) return;
2062
+ pendingProjectInitialization = initialization;
2063
+ if (initialization.mode === "apply") {
2064
+ setControlState(ctx, executingState("执行已确认的项目初始化"));
2065
+ activeWorkflowTurn = true;
2066
+ await pi.sendUserMessage(projectInitializationDirective(initialization.summary));
2067
+ return;
2068
+ }
1786
2069
  setControlState(ctx, planningState("project-init"));
1787
2070
  await pi.sendUserMessage(
1788
2071
  [
@@ -1790,7 +2073,7 @@ export default function devflowCore(pi: ExtensionAPI) {
1790
2073
  "用户已确认项目初始化的输入。你现在是 Planner,只允许只读分析和派发 project-scout / requirements-auditor / contract-auditor。",
1791
2074
  "不得创建 .planning 文件、不得调用状态迁移、不得执行实现。",
1792
2075
  "先输出:已知事实、待验证点、最少侦察任务和必须由用户决定的问题。没有证据时写 unknown。",
1793
- summary,
2076
+ initialization.summary,
1794
2077
  "</devflow-planner-directive>",
1795
2078
  ].join("\n"),
1796
2079
  );
@@ -117,6 +117,7 @@ After each step completes, verify the step's gate before advancing:
117
117
  | requirement-feasibility-review | 可实现性审查.meta.json `status=ready_for_baseline`、`baseline_gate=pass`、`gap_count=0` 且无 critical finding | Reject advance to requirement-baseline |
118
118
  | requirement-baseline | baseline.md review conclusion is "通过" AND baseline.meta.json `review_status` / `traceability_status` are `passed` AND blocking counters are all 0 | Reject advance to api-contract |
119
119
  | api-contract | api-contract.meta.json `review_status` / `traceability_status` are `passed` AND `pending_contract_count` is 0 | Reject advance to dual tracks |
120
+ | architecture-feasibility | Core-derived `architecture-brief.md` / meta exist. If `assessment_level=full`, require `assessment_status=ready` and `comparison_status=confirmed`; a lightweight incomplete result may proceed only as an explicitly unresolved downstream input. | Reject advance to dual tracks for an incomplete full assessment |
120
121
  | ui-spec | UI-SPEC.md review conclusion is "通过" | Reject advance to feature-breakdown |
121
122
  | feature-breakdown | 功能点梳理.md review conclusion is "通过" | Reject advance to tech-specs |
122
123
  | api-call-guide | api-call-guide.meta.json `frontend_consumable` and `mock_ready` are true, pending_items is empty | Reject merge gate |
@@ -161,8 +162,8 @@ When the inspector returns `route_failure_branch`:
161
162
  When the inspector returns `run_parallel_window`:
162
163
 
163
164
  0. **Track filtering**(新增):读取 STATE.md 的 `active_track` 字段
164
- - 如果 `active_track == "A"`:只路由完整需求主链及 Track A 步骤(requirement-triage、requirement-wayfinder、domain-modeling、requirement-feasibility-review、requirement-baseline、api-contract、backend-tech-spec、api-call-guide),完成后阻断在轨道边界
165
- - 如果 `active_track == "B"`:新需求必须完成完整需求主链;前端设计步骤仍依赖 api-contract。允许步骤为 requirement-triage、requirement-wayfinder、domain-modeling、requirement-feasibility-review、requirement-baseline、api-contract、ui-spec、feature-breakdown、frontend-tech-spec、api-supplement,完成后阻断在轨道边界
165
+ - 如果 `active_track == "A"`:只路由完整需求主链及 Track A 步骤(requirement-triage、requirement-wayfinder、domain-modeling、requirement-feasibility-review、requirement-baseline、api-contract、architecture-feasibility、backend-tech-spec、api-call-guide),完成后阻断在轨道边界
166
+ - 如果 `active_track == "B"`:新需求必须完成完整需求主链;前端设计步骤仍依赖 api-contract 和 architecture-feasibility。允许步骤为 requirement-triage、requirement-wayfinder、domain-modeling、requirement-feasibility-review、requirement-baseline、api-contract、architecture-feasibility、ui-spec、feature-breakdown、frontend-tech-spec、api-supplement,完成后阻断在轨道边界
166
167
  - 如果未设置或无效:保留全部 branch_steps(双轨道)
167
168
  - 过滤后如果只剩一个步骤,退化为串行执行(不启动并行窗口)
168
169