@guanlin4924/devflow-pi 0.1.15 → 0.1.17

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 (30) hide show
  1. package/package.json +1 -1
  2. package/plugins/devflow-core/extensions/devflow-core.ts +425 -54
  3. package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +3 -2
  4. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_assessment.rb +458 -0
  5. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_brief.rb +256 -0
  6. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_constraints.rb +155 -0
  7. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_feasibility.rb +176 -0
  8. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +46 -0
  9. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +94 -0
  10. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_state.rb +2 -1
  11. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/machine_evidence.rb +112 -0
  12. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +646 -0
  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 +140 -0
  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 +88 -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
  25. package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +4 -0
  26. package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +4 -0
  27. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +4 -0
  28. package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +4 -0
  29. package/src/cli.ts +2 -1
  30. package/src/runtime.ts +2 -1
@@ -6,6 +6,8 @@ require "optparse"
6
6
  require "yaml"
7
7
  require_relative "control_io"
8
8
  require_relative "phase_ownership"
9
+ require_relative "requirement_graph"
10
+ require_relative "architecture_feasibility"
9
11
 
10
12
  module WorkflowStateValidator
11
13
  REQUIRED_STATE_FIELDS = %w[
@@ -76,6 +78,26 @@ module WorkflowStateValidator
76
78
  validate_completed_outputs(issues, root, state, step_by_id, completed_ids, artifact_by_path, strict_artifact_status)
77
79
  validate_waiting_checkpoint(issues, root, state, step_by_id, artifact_by_path, strict_artifact_status)
78
80
  validate_current_in_progress_output_drift(issues, root, state, step_by_id, artifact_by_path)
81
+ graph_gate = workflow.dig("governance", "requirement_graph_gate").to_s
82
+ governed_steps = %w[requirement-baseline api-contract architecture-feasibility integration-verify change-plan execute verify]
83
+ # A historical completed state is never rolled back solely because it has
84
+ # no v2 graph/evidence. Its first new governed step, however, must satisfy
85
+ # the new contract before it can advance.
86
+ strict_requirement_graph = graph_gate == "enforced" && (governed_steps.include?(state["current_step"]) || completed_ids.any? { |id| governed_steps.include?(id) })
87
+ observe_requirement_graph(issues, root, state, strict: strict_requirement_graph)
88
+ # Migration is intentionally non-destructive: pre-v2 completed steps are
89
+ # visible as unattested, never rewritten, rolled back, or auto-rerun.
90
+ if state["governance_started_revision"].nil?
91
+ legacy_steps = completed_ids.select { |id| governed_steps.include?(id) }
92
+ if legacy_steps.any?
93
+ issues << issue(
94
+ "warning",
95
+ "legacy_unattested",
96
+ "Historical governed steps predate Evidence Pack v2 and are report-only",
97
+ steps: legacy_steps
98
+ )
99
+ end
100
+ end
79
101
 
80
102
  ok = issues.none? { |entry| entry["severity"] == "error" }
81
103
  result(ok, state_path, workflow_path, registry_path, issues)
@@ -263,6 +285,43 @@ module WorkflowStateValidator
263
285
  end
264
286
  end
265
287
 
288
+ # P1 observation mode: existing projects must not become invalid merely
289
+ # because they predate governance graphs. Once a graph exists, surface its
290
+ # structural failures through state inspection so operators can fix them
291
+ # before the corresponding checkpoint is upgraded to fail-closed.
292
+ def observe_requirement_graph(issues, root, state, strict: false)
293
+ path, relative = RequirementGraph.graph_path(root, state["current_feature"])
294
+ unless File.exist?(path)
295
+ issues << issue(strict ? "error" : "warning", "requirement_graph_missing", "Requirement governance graph is missing", path: relative) if strict
296
+ return
297
+ end
298
+
299
+ graph, = RequirementGraph.load_graph(root, state["current_feature"])
300
+ errors = RequirementGraph.validate(graph)
301
+ if errors.any?
302
+ issues << issue(
303
+ strict ? "error" : "warning",
304
+ "requirement_graph_observe_failed",
305
+ "Requirement governance graph has structural gaps (observation mode)",
306
+ path: relative,
307
+ graph_revision: graph["revision"],
308
+ errors: errors
309
+ )
310
+ end
311
+ return if Array(graph["stale"]).empty?
312
+
313
+ issues << issue(
314
+ strict ? "error" : "warning",
315
+ "requirement_graph_needs_recheck",
316
+ "Requirement governance graph has downstream nodes awaiting recheck (observation mode)",
317
+ path: relative,
318
+ graph_revision: graph["revision"],
319
+ stale: graph["stale"]
320
+ )
321
+ rescue ArgumentError => error
322
+ issues << issue("warning", "requirement_graph_observe_failed", error.message)
323
+ end
324
+
266
325
  def validate_step_outputs(issues, root, state, step, artifact_by_path, strict_artifact_status, waiting_checkpoint)
267
326
  Array(step["outputs"]).each do |output|
268
327
  next unless output["required"]
@@ -320,6 +379,8 @@ module WorkflowStateValidator
320
379
  validate_requirement_feasibility_review_gate(issues, step, data)
321
380
  validate_requirement_baseline_gate(issues, step, data)
322
381
  validate_api_contract_gate(issues, step, data)
382
+ validate_architecture_feasibility_gate(issues, step, data)
383
+ validate_architecture_consumption_gate(issues, root, state, step, data)
323
384
  validate_api_call_guide_gate(issues, step, data)
324
385
  validate_frontend_tech_spec_gate(issues, step, data)
325
386
  validate_api_supplement_gate(issues, step, data)
@@ -454,6 +515,85 @@ module WorkflowStateValidator
454
515
  end
455
516
  end
456
517
 
518
+ # P2 is intentionally graduated: only a structurally-derived full
519
+ # assessment fail-closes. Lightweight incomplete work remains visible to its
520
+ # technical-spec consumers rather than forcing every small feature through
521
+ # an invented NFR exercise.
522
+ def validate_architecture_feasibility_gate(issues, step, data)
523
+ return unless step["id"] == "architecture-feasibility"
524
+
525
+ level = data["assessment_level"]
526
+ status = data["assessment_status"]
527
+ comparison = data["comparison_status"]
528
+ unless %w[not_triggered lightweight full].include?(level)
529
+ issues << issue("error", "architecture_assessment_level_invalid", "Architecture assessment level is invalid", step: step["id"], level: level)
530
+ return
531
+ end
532
+ unless %w[ready incomplete].include?(status)
533
+ issues << issue("error", "architecture_assessment_status_invalid", "Architecture assessment status is invalid", step: step["id"], status: status)
534
+ return
535
+ end
536
+ unless %w[not_required missing awaiting_user_confirmation missing_risk awaiting_risk_confirmation confirmed].include?(comparison)
537
+ issues << issue("error", "architecture_comparison_status_invalid", "Architecture comparison status is invalid", step: step["id"], comparison_status: comparison)
538
+ return
539
+ end
540
+ return unless level == "full"
541
+ return if status == "ready" && comparison == "confirmed"
542
+
543
+ issues << issue(
544
+ "error",
545
+ "architecture_full_assessment_incomplete",
546
+ "A full architecture assessment requires ready inputs plus confirmed decision and risk treatment before technical specifications.",
547
+ step: step["id"],
548
+ assessment_status: status,
549
+ comparison_status: comparison
550
+ )
551
+ end
552
+
553
+ # A technical spec does not merely claim that it considered architecture. It
554
+ # pins the exact Core-derived brief it consumed. Compare the pin both to the
555
+ # brief and to the current graph/context so an upstream edit cannot leave a
556
+ # convincing but obsolete spec looking fresh.
557
+ def validate_architecture_consumption_gate(issues, root, state, step, data)
558
+ return unless %w[frontend-tech-spec backend-tech-spec].include?(step["id"])
559
+
560
+ feature = state["current_feature"]
561
+ brief_path = File.join(root, ".planning", "features", feature, "architecture-brief.meta.json")
562
+ unless File.file?(brief_path)
563
+ issues << issue("error", "architecture_brief_meta_missing", "Technical spec cannot be validated without the architecture brief meta", step: step["id"], path: brief_path)
564
+ return
565
+ end
566
+ brief = JSON.parse(File.read(brief_path))
567
+ expected = {
568
+ "architecture_brief_revision" => brief["brief_revision"],
569
+ "architecture_graph_revision" => brief["graph_revision"],
570
+ "architecture_static_context_snapshot_revision" => brief["static_context_snapshot_revision"],
571
+ "architecture_comparison_status" => brief["comparison_status"]
572
+ }
573
+ expected.each do |field, value|
574
+ next if data[field] == value
575
+
576
+ issues << issue("error", "architecture_consumption_pin_mismatch", "Technical spec architecture pin differs from the Core-derived brief", step: step["id"], field: field, spec_value: data[field], brief_value: value)
577
+ end
578
+
579
+ graph, = RequirementGraph.load_graph(root, feature)
580
+ if brief["graph_revision"] != graph["revision"]
581
+ issues << issue("error", "architecture_brief_graph_stale", "Architecture brief was derived from a stale graph revision", step: step["id"], brief_revision: brief["graph_revision"], graph_revision: graph["revision"])
582
+ end
583
+ current_context = ArchitectureFeasibility.safe_static_context(root)
584
+ current_context_revision = current_context["available"] ? current_context["snapshot_revision"] : nil
585
+ if brief["static_context_snapshot_revision"] != current_context_revision
586
+ issues << issue("error", "architecture_brief_context_stale", "Architecture brief was derived from a stale static-context snapshot", step: step["id"], brief_revision: brief["static_context_snapshot_revision"], context_revision: current_context_revision)
587
+ end
588
+ if brief["assessment_level"] == "full" && brief["comparison_status"] != "confirmed"
589
+ issues << issue("error", "architecture_brief_full_unconfirmed", "Technical spec cannot consume an unconfirmed full architecture assessment", step: step["id"], comparison_status: brief["comparison_status"])
590
+ end
591
+ rescue JSON::ParserError => error
592
+ issues << issue("error", "architecture_brief_meta_invalid", error.message, step: step["id"], path: brief_path)
593
+ rescue ArgumentError => error
594
+ issues << issue("error", "architecture_brief_graph_invalid", error.message, step: step["id"])
595
+ end
596
+
457
597
  def validate_api_call_guide_gate(issues, step, data)
458
598
  return unless step["id"] == "api-call-guide"
459
599
 
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Workload constraint validator (P1 NFR). Source-status driven: measured /
5
+ # contracted require evidence; approved_assumption requires owner +
6
+ # observability + review date + escalation trigger; unknown carries no
7
+ # evidence requirement but is only advisory.
8
+ require "json"
9
+ require "optparse"
10
+
11
+ module WorkloadConstraint
12
+ SOURCE_STATUSES = %w[measured contracted approved_assumption unknown].freeze
13
+ METRICS = %w[peak_qps data_growth rpo rto p95_latency].freeze
14
+ APPROVED_ASSUMPTION_FIELDS = %w[owner observability_metric review_date escalation_trigger].freeze
15
+
16
+ module_function
17
+
18
+ def validate(constraint)
19
+ errors = []
20
+ errors << "workload_ref is required" if constraint["workload_ref"].to_s.empty?
21
+ unless constraint["workload_ref"].to_s.match?(/\A(BEH|SL|API)-\d+\z/)
22
+ errors << "workload_ref must reference BEH/SL/API"
23
+ end
24
+ errors << "metric is required" if constraint["metric"].to_s.empty?
25
+ errors << "invalid metric" if !constraint["metric"].to_s.empty? && !METRICS.include?(constraint["metric"])
26
+ errors << "source_status is required" if constraint["source_status"].to_s.empty?
27
+ errors << "invalid source_status" if !constraint["source_status"].to_s.empty? && !SOURCE_STATUSES.include?(constraint["source_status"])
28
+ target = constraint["target"]
29
+ errors << "target.value is required" unless target.is_a?(Hash) && !target["value"].to_s.empty?
30
+ case constraint["source_status"]
31
+ when "measured", "contracted"
32
+ errors << "evidence_ref is required for #{constraint["source_status"]}" if constraint["evidence_ref"].to_s.empty?
33
+ when "approved_assumption"
34
+ APPROVED_ASSUMPTION_FIELDS.each do |key|
35
+ errors << "approved_assumption requires #{key}" if constraint[key].to_s.empty?
36
+ end
37
+ end
38
+ errors.uniq
39
+ end
40
+ end
41
+
42
+ if __FILE__ == $PROGRAM_NAME
43
+ options = { constraint_json: nil }
44
+ command = ARGV.shift
45
+ OptionParser.new do |o|
46
+ o.on("--constraint-json JSON") { |v| options[:constraint_json] = v }
47
+ o.on("--json") { options[:json] = true }
48
+ end.parse!
49
+ begin
50
+ raise ArgumentError, "command must be validate" unless command == "validate"
51
+ constraint = JSON.parse(options[:constraint_json].to_s)
52
+ errors = WorkloadConstraint.validate(constraint)
53
+ { "ok" => errors.empty?, "errors" => errors }
54
+ rescue StandardError => error
55
+ { "ok" => false, "reason" => "workload_constraint_failed", "message" => error.message }
56
+ end.tap { |r| puts JSON.pretty_generate(r) }
57
+ end
@@ -13,6 +13,43 @@ phase_scope:
13
13
  failure_branch_enabled: true
14
14
  parallel_window_enabled: true
15
15
 
16
+ # P2 preset: new work fail-closes at the defined checkpoints. Existing STATE
17
+ # files without governance_started_revision remain legacy_unattested and are
18
+ # only reported until their next newly-started governed step.
19
+ governance:
20
+ requirement_graph_gate: enforced
21
+ enforced_checkpoints:
22
+ - CP-1
23
+ - CP-API
24
+ - CP-2
25
+ - CP-3
26
+ step_checkers:
27
+ requirement-baseline:
28
+ - requirement-graph-check
29
+ - slice-independence-check
30
+ - acceptance-coverage-check
31
+ - change-impact-check
32
+ api-contract:
33
+ - requirement-graph-check
34
+ - contract-coverage-check
35
+ - change-impact-check
36
+ architecture-feasibility:
37
+ - requirement-graph-check
38
+ - change-impact-check
39
+ integration-verify:
40
+ - requirement-graph-check
41
+ - slice-independence-check
42
+ - contract-coverage-check
43
+ - change-impact-check
44
+ change-plan:
45
+ - requirement-graph-check
46
+ - slice-independence-check
47
+ - contract-coverage-check
48
+ - change-impact-check
49
+ verify:
50
+ - acceptance-coverage-check
51
+ - change-impact-check
52
+
16
53
  artifact_registry:
17
54
  source: ".planning/artifacts/index.yaml"
18
55
  path_key: path
@@ -44,7 +81,7 @@ lessons_policy:
44
81
  parallel_windows:
45
82
  - id: dual-track-design
46
83
  label: API contract based frontend/backend design parallel window
47
- after_step: api-contract
84
+ after_step: architecture-feasibility
48
85
  branch_steps:
49
86
  - backend-tech-spec
50
87
  - ui-spec
@@ -184,7 +221,7 @@ checkpoints:
184
221
  - approve
185
222
  - needs_fix
186
223
  skip_policy: never
187
- resume_behavior: "user confirms, then enter Track A/Track B design"
224
+ resume_behavior: "user confirms, then run architecture feasibility review before Track A/Track B design"
188
225
  CP-2:
189
226
  name: Merge Gate confirmation
190
227
  gate_after: integration-verify
@@ -438,12 +475,44 @@ steps:
438
475
  traceability_status: { type: string, required: true }
439
476
  checkpoint: CP-API
440
477
 
478
+ - id: architecture-feasibility
479
+ order: 18
480
+ label: Architecture feasibility
481
+ skill: workflow-architecture-feasibility
482
+ depends_on:
483
+ - api-contract
484
+ inputs:
485
+ - ".planning/requirements/baseline.md"
486
+ - ".planning/features/{feature}/api-contract.md"
487
+ - ".planning/requirements/{feature}/governance.json"
488
+ - ".context/static-runtime-context.json"
489
+ - ".planning/navigation/INDEX.md"
490
+ outputs:
491
+ - path: ".planning/features/{feature}/architecture-brief.md"
492
+ type: architecture-brief
493
+ required: true
494
+ meta_output:
495
+ path: ".planning/features/{feature}/architecture-brief.meta.json"
496
+ fields:
497
+ assessment_status: { type: string, required: true }
498
+ assessment_level: { type: string, required: true }
499
+ graph_revision: { type: integer, required: true }
500
+ static_context_snapshot_revision: { type: integer, required: false }
501
+ comparison_status: { type: string, required: true }
502
+ trigger_codes: { type: array, required: true }
503
+ unresolved_codes: { type: array, required: true }
504
+ decision_ids: { type: array, required: true }
505
+ confirmed_decision_ids: { type: array, required: true }
506
+ risk_ids: { type: array, required: true }
507
+ confirmed_risk_ids: { type: array, required: true }
508
+
441
509
  - id: ui-spec
442
510
  order: 20
443
511
  label: UI spec
444
512
  skill: workflow-ui-spec
445
513
  depends_on:
446
514
  - api-contract
515
+ - architecture-feasibility
447
516
  inputs:
448
517
  - ".planning/requirements/baseline.md"
449
518
  - ".planning/features/{feature}/api-contract.md"
@@ -474,8 +543,11 @@ steps:
474
543
  depends_on:
475
544
  - feature-breakdown
476
545
  - api-contract
546
+ - architecture-feasibility
477
547
  inputs:
478
548
  - ".planning/features/{feature}/api-contract.md"
549
+ - ".planning/features/{feature}/architecture-brief.md"
550
+ - ".planning/features/{feature}/architecture-brief.meta.json"
479
551
  - ".planning/features/{feature}/功能点梳理.md"
480
552
  - ".planning/features/{feature}/UI-SPEC.md"
481
553
  - ".planning/navigation/INDEX.md"
@@ -492,6 +564,10 @@ steps:
492
564
  component_count: { type: integer, required: true }
493
565
  api_count: { type: integer, required: true }
494
566
  api_gaps_count: { type: integer, required: true }
567
+ architecture_brief_revision: { type: integer, required: true }
568
+ architecture_graph_revision: { type: integer, required: true }
569
+ architecture_static_context_snapshot_revision: { type: integer, required: false }
570
+ architecture_comparison_status: { type: string, required: true }
495
571
 
496
572
  - id: backend-tech-spec
497
573
  order: 50
@@ -499,8 +575,11 @@ steps:
499
575
  skill: workflow-backend-tech-spec
500
576
  depends_on:
501
577
  - api-contract
578
+ - architecture-feasibility
502
579
  inputs:
503
580
  - ".planning/features/{feature}/api-contract.md"
581
+ - ".planning/features/{feature}/architecture-brief.md"
582
+ - ".planning/features/{feature}/architecture-brief.meta.json"
504
583
  - ".planning/requirements/baseline.md"
505
584
  - ".planning/navigation/INDEX.md"
506
585
  - ".planning/execution-contract.md"
@@ -509,6 +588,13 @@ steps:
509
588
  - path: ".planning/features/{feature}/后端技术方案.md"
510
589
  type: backend-tech-spec
511
590
  required: true
591
+ meta_output:
592
+ path: ".planning/features/{feature}/后端技术方案.meta.json"
593
+ fields:
594
+ architecture_brief_revision: { type: integer, required: true }
595
+ architecture_graph_revision: { type: integer, required: true }
596
+ architecture_static_context_snapshot_revision: { type: integer, required: false }
597
+ architecture_comparison_status: { type: string, required: true }
512
598
 
513
599
  - id: api-call-guide
514
600
  order: 55
@@ -29,6 +29,7 @@ pipeline 本身不包含执行规则——所有门禁、CP 协议、失败分
29
29
  | 8 | requirement-feasibility-review | 可实现性审查 |
30
30
  | 10 | requirement-baseline | 需求基线 |
31
31
  | 15 | api-contract | API 契约(分叉前共同事实) |
32
+ | 18 | architecture-feasibility | 架构可行性与方案比较(分叉前共同事实) |
32
33
  | 20 | ui-spec | Track B:UI 设计规范 |
33
34
  | 30 | feature-breakdown | Track B:功能点梳理 |
34
35
  | 40 | backend-tech-spec | Track A:后端方案 |
@@ -46,8 +47,8 @@ pipeline 本身不包含执行规则——所有门禁、CP 协议、失败分
46
47
  | 110 | update-navigation | 更新导航 |
47
48
  | 120 | archive | 里程碑归档 |
48
49
 
49
- - **`--track A`**:后端/API 轨道产物模式。可跑 `requirement-triage → requirement-wayfinder → domain-modeling → requirement-feasibility-review → requirement-baseline → api-contract → backend-tech-spec → api-call-guide`,但不能自动进入 `integration-verify / change-plan / execute`。
50
- - **`--track B`**:前端/UI 轨道产物模式。要求 `api-contract` 已存在且 approved;新需求从需求主链开始,已有契约可跑 `ui-spec → feature-breakdown → frontend-tech-spec`,但不能自动进入 `change-plan / execute`。
50
+ - **`--track A`**:后端/API 轨道产物模式。可跑 `requirement-triage → requirement-wayfinder → domain-modeling → requirement-feasibility-review → requirement-baseline → api-contract → architecture-feasibility → backend-tech-spec → api-call-guide`,但不能自动进入 `integration-verify / change-plan / execute`。
51
+ - **`--track B`**:前端/UI 轨道产物模式。要求 `api-contract` 与 `architecture-feasibility` 已完成;新需求从需求主链开始,已有共同事实可跑 `ui-spec → feature-breakdown → frontend-tech-spec`,但不能自动进入 `change-plan / execute`。
51
52
  - **未设置 track**:完整交付模式。必须完成双轨并通过 `integration-verify` 后才能进入 `change-plan`。
52
53
  - **`--no-review`**:保留参数位,但当前双轨 workflow 不自动跳过 CP-2。Merge Gate 不能跳过。
53
54
 
@@ -35,6 +35,7 @@ Always create or update:
35
35
  - `.planning/navigation/INDEX.md` — codebase registry: maps each codebase to its navigation file, plus rule package index and downstream read contract
36
36
  - `.planning/navigation/{name}.md` — one file per codebase: framework, entries, conventions, commands (name comes from user-provided codebase name)
37
37
  - `.planning/code-index/` — machine code intelligence index location; generated by `workflow-code-index` by default unless fast mode skips it
38
+ - `.context/static-runtime-context/` + `.context/static-runtime-context.json` — Core-owned, Git-bound static declarations snapshot; never a production-health or capacity assertion
38
39
  - `.context/spec/design/` — conditional frontend design rule package; installed after code-index for projects with frontend codebases
39
40
  - `.planning/project-context-run.json` — current-run evidence: run identity, code-index status, dynamically discovered depth specs, source paths, and source fingerprints
40
41
  - `.context/spec/index.md` — generated technical specification registry: maps stack profiles and codebase specs
@@ -114,7 +115,7 @@ If `.planning/` already exists:
114
115
 
115
116
  1. Read existing `.planning/PROJECT.md`, `.planning/STATE.md`, and `.planning/navigation/*.md` before writing.
116
117
  2. Preserve user-authored business context, constraints, key decisions, and missing information unless the user explicitly asks to replace them.
117
- 3. Refresh only machine-derived fields. This includes: detected paths, frameworks, entries, commands, scan status, navigation index, rule package files (execution-contract.md, toolchain.md, rules/INDEX.md), **all currently discovered depth spec files (Step 7.4)**, and **code index (Step 6.5)**. The presence of existing output files does NOT exempt a step from re-execution — machine-derived fields must be regenerated to reflect current codebase state.
118
+ 3. Refresh only machine-derived fields. This includes: detected paths, frameworks, entries, commands, scan status, navigation index, rule package files (execution-contract.md, toolchain.md, rules/INDEX.md), **all currently discovered depth spec files (Step 7.4)**, **code index (Step 6.5)**, and **static runtime-context (Step 6.5a)**. The presence of existing output files does NOT exempt a step from re-execution — machine-derived fields must be regenerated to reflect current codebase state.
118
119
  4. If provided codebase locations differ from existing records, record the change in `STATE.md` under `Change Log`.
119
120
  5. Never delete existing planning files during initialization. Preserve user-authored content in execution-contract.md and toolchain.md when refreshing machine-derived fields.
120
121
 
@@ -478,6 +479,21 @@ After full-mode commands succeed, set `code_index_status` to `complete`, use ski
478
479
 
479
480
  Do not require users to manually run Python when they invoked this skill.
480
481
 
482
+ ## Step 6.5a — Static Runtime Context Snapshot (REQUIRED)
483
+
484
+ After Step 6.5, collect the separate P1 static-context snapshot:
485
+
486
+ ```text
487
+ devflow_static_runtime_context(action="collect")
488
+ devflow_static_runtime_context(action="inspect")
489
+ ```
490
+
491
+ Run this in both full and fast modes. It reads the committed Git tree and may therefore collect manifest declarations even when code-index was intentionally skipped. Verify that the returned pointer exists and record the snapshot revision in the initialization summary.
492
+
493
+ Do not describe this snapshot as production topology, resource health, capacity, or a list of deployed services. Its P1 scope is repository declarations and explicitly supported platform manifests only; unsupported resource bindings, integrations, and jobs remain `unknown` until a later collector can prove them.
494
+
495
+ If the repository has no readable Git commit, retain the generated snapshot with incomplete coverage and record the limitation. Do not invent a working-tree baseline or silently skip the collection.
496
+
481
497
  ## Step 6.6 — Design Rule Package (Conditional Required)
482
498
 
483
499
  This step belongs to project initialization, not feature design. It runs **after the code index is available** and before the context-spec package is finalized.
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: workflow-architecture-feasibility
3
+ description: "Use after API contract and before frontend/backend technical specifications to compare implementation paths against confirmed workload facts and repository-static context. Use for async or scheduled work, batch operations, external calls, fanout, migrations, workload/SLA constraints, or when a feature may not fit the current framework and integrations."
4
+ ---
5
+
6
+ # 架构可行性与方案比较
7
+
8
+ 生成一份由 Core 派生的架构简报;不把自由 Markdown 当作架构真相,也不把仓库声明当成生产容量。
9
+
10
+ ## 固定流程
11
+
12
+ 1. 读取 `devflow_requirement_graph(action="inspect")`、`devflow_static_runtime_context(action="inspect")`、API 契约和需求基线。
13
+ 2. 调用 `devflow_architecture_feasibility(feature=...)`。保留返回的 graph revision、static snapshot revision、触发码和未决码;不得自行改写等级。
14
+ 3. 对命中的工作负载补充 candidate:先用 `devflow_architecture_constraint` 校验,再用 `devflow_requirement_graph(action="propose")` 写入。
15
+ - WSH 使用 `workload_shape`,并连接 `REQ/BEH/API → WSH`,relation 为 `shapes_workload`。
16
+ - WLC 使用 `workload_constraint`,并连接 `BEH/SL/API → WLC`,relation 为 `constrains_workload`。
17
+ - `unknown` 只能写缺口、责任人、截止 checkpoint;不能填 target、evidence 或 assumption。
18
+ 4. 对 `lightweight` 或 `full` 且存在实质取舍的结果,提交 `decision_type: architecture_option_comparison` 的 `DECISION` candidate:
19
+ - `subject_refs` 只引用 WSH/WLC;WSH 用 `informs_decision` 连到 DECISION,WLC 用 `constrains_decision` 连到 DECISION。
20
+ - 每个 option 使用 `option_key`,并包含 `mechanism`、非空 `fact_refs`、非空 `tradeoffs`。
21
+ - `multiple_options` 至少两个机制不同的选项;没有实质替代时使用 `single_path`,不要凑 A/B/C。
22
+ - `recommendation_option_id` 是建议,不是自动决策。
23
+ 5. 对 `full` 评估,提交 `risk_type: architecture_option_risk` 的 `RISK` candidate,连接 `DECISION/WSH/WLC → RISK`,relation 为 `has_architecture_risk`。必须写失败模式、影响、处理、处理方式、负责人和复核条件。
24
+ 6. 只用 `devflow_requirement_graph(action="confirm")` 确认 WSH/WLC、架构 DECISION 和架构 RISK。Core 必须展示确认对话;确认方案比较时,用户确认的是显示的 `recommendation_option_id`,确认风险时只确认处理责任,不宣称生产风险已消失。
25
+ 7. 调用 `devflow_architecture_brief(action="render")` 生成 `.planning/features/{feature}/architecture-brief.md` 及 meta。不得手写或补写简报正文。
26
+
27
+ ## 不变量
28
+
29
+ - 静态上下文只说明提交到仓库的声明/引用;不说明生产资源、健康度、容量或监控余量。
30
+ - 数字没有 measured/contracted 证据时,保持 unknown;不得为通过检查编 QPS、DAU、数据量或阈值。
31
+ - `assessment_status: incomplete`、`comparison_status: missing` 或 `awaiting_*` 必须原样交给下游技术方案,不能写成“评估通过”。
32
+ - 已确认 WSH/WLC/架构 DECISION/RISK 不可原地更新;新建 candidate、重新连边,并让 stale 传播要求消费者复核。
33
+ - 不替用户选择方案、接受残余风险或伪造 CP 记录。
34
+
35
+ ## 下游交接
36
+
37
+ 前端和后端技术方案都必须读取 `architecture-brief.md` 和 `.meta.json`,并记录消费的 `graph_revision`、`static_context_snapshot_revision`、`brief_revision`。若 brief 不是 `comparison_status: confirmed`,只可把其未决项作为前提/风险写入方案,不能把推荐项变成已决定实现。
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Architecture Feasibility"
3
+ short_description: "Compare architecture options from confirmed constraints."
4
+ default_prompt: "Use $workflow-architecture-feasibility to compare evidence-bound implementation options before technical specs."
@@ -13,6 +13,8 @@ user-invocable: true
13
13
  pattern: generator+tool-wrapper
14
14
  inputs:
15
15
  - .planning/features/{功能名}/api-contract.md
16
+ - .planning/features/{功能名}/architecture-brief.md
17
+ - .planning/features/{功能名}/architecture-brief.meta.json
16
18
  - .planning/requirements/baseline.md
17
19
  - .planning/features/{功能名}/功能点梳理.md (optional, Track B context when already available)
18
20
  - .planning/PROJECT.md
@@ -73,8 +75,9 @@ allowed-tools:
73
75
  - 影响 scope、语义、归属、兼容性或实现成本的未确认项,不得被当成已确认需求
74
76
  - 若未确认项阻塞后端方案,询问用户;`--auto` 或无法询问时写入待确认事项和风险
75
77
  7. 读取 `.planning/features/{功能名}/api-contract.md`。在双轨模式下,后端方案必须以 API 契约和需求基线作为权威输入,不得等待 Track B 的 `功能点梳理.md` 才开始。
76
- 8. 若 `功能点梳理.md` 已存在,可作为测试可读上下文补充读取;若不存在,不阻塞 Track A。
77
- 9. 若 baseline、api-contract 或 `功能点梳理.md` 包含 `SL-*` 切片计划,读取 `skill://workflow-pipeline/references/slice-safety-guide.md`,只为当前执行切片生成后端方案。后置切片只可作为依赖上下文或后续范围,不得生成库表、接口、任务或运行时设计。
78
+ 8. 读取 `.planning/features/{功能名}/architecture-brief.md` 和 `.meta.json`,在文档开头记录消费的 `brief_revision`、`graph_revision` 和 `static_context_snapshot_revision`。`comparison_status=confirmed` 的选择可作为实现依据;其他状态只能作为风险或待确认前提,不得把 recommendation 写成已决定实现。若 `assessment_level=full` 但未 confirmed,停止并回到架构评估。
79
+ 9. 若 `功能点梳理.md` 已存在,可作为测试可读上下文补充读取;若不存在,不阻塞 Track A。
80
+ 10. 若 baseline、api-contract 或 `功能点梳理.md` 包含 `SL-*` 切片计划,读取 `skill://workflow-pipeline/references/slice-safety-guide.md`,只为当前执行切片生成后端方案。后置切片只可作为依赖上下文或后续范围,不得生成库表、接口、任务或运行时设计。
78
81
 
79
82
  ### 第二阶段:探索现有代码
80
83
 
@@ -208,6 +211,8 @@ If an older baseline still points to root `features/{功能名}/`, treat that as
208
211
 
209
212
  ### 第五阶段:结构化元数据
210
213
 
214
+ 完成后写入 `.planning/features/{功能名}/后端技术方案.meta.json`。必须从 `architecture-brief.meta.json` 原样复制:`brief_revision → architecture_brief_revision`、`graph_revision → architecture_graph_revision`、`static_context_snapshot_revision → architecture_static_context_snapshot_revision`、`comparison_status → architecture_comparison_status`。不得凭记忆填写或改写这些值。
215
+
211
216
  完成后写入 `.planning/features/{功能名}/后端技术方案.meta.json`,字段定义见 `feature-development.yaml` 中 `steps[backend-tech-spec].meta_output.fields`:`api_count`, `table_count`, `migration_count`。
212
217
 
213
218
  ## 注意事项
@@ -14,6 +14,8 @@ pattern: generator+tool-wrapper
14
14
  inputs:
15
15
  - .planning/features/{功能名}/功能点梳理.md
16
16
  - .planning/features/{功能名}/UI-SPEC.md
17
+ - .planning/features/{功能名}/architecture-brief.md
18
+ - .planning/features/{功能名}/architecture-brief.meta.json
17
19
  - .planning/requirements/baseline.md
18
20
  - .planning/PROJECT.md
19
21
  - .planning/navigation/INDEX.md
@@ -72,7 +74,8 @@ allowed-tools:
72
74
  - 已确认的页面/交互/复用/替代方案决策必须进入前端方案
73
75
  - 影响 scope、交互语义、组件归属、兼容性或实现成本的未确认项,不得被当成已确认需求
74
76
  - 若未确认项阻塞前端方案,询问用户;`--auto` 或无法询问时写入待确认事项和风险
75
- 9. 若 baseline 或 `功能点梳理.md` 包含 `SL-*` 切片计划,读取 `skill://workflow-pipeline/references/slice-safety-guide.md`,只为当前执行切片生成前端方案。后置切片只可作为依赖上下文或后续范围,不得生成路由、组件或 API 调用实现设计。
77
+ 9. 读取 `.planning/features/{功能名}/architecture-brief.md` 和 `.meta.json`,在文档开头记录消费的 `brief_revision`、`graph_revision` 和 `static_context_snapshot_revision`。只有 `comparison_status=confirmed` 的架构选择可改变请求编排、缓存、轮询或异步交互;未确认/未决项必须保留为风险或待确认事项。
78
+ 10. 若 baseline 或 `功能点梳理.md` 包含 `SL-*` 切片计划,读取 `skill://workflow-pipeline/references/slice-safety-guide.md`,只为当前执行切片生成前端方案。后置切片只可作为依赖上下文或后续范围,不得生成路由、组件或 API 调用实现设计。
76
79
 
77
80
  ### 第二阶段:探索现有前端代码
78
81
 
@@ -170,6 +173,8 @@ If an older baseline still points to root `features/{功能名}/`, treat that as
170
173
 
171
174
  完成后写入 `.planning/features/{功能名}/前端技术方案.meta.json`,字段定义见 `feature-development.yaml` 中 `steps[frontend-tech-spec].meta_output.fields`:`route_count`, `component_count`, `api_count`, `api_gaps_count`。
172
175
 
176
+ 同时必须从 `architecture-brief.meta.json` 原样复制:`brief_revision → architecture_brief_revision`、`graph_revision → architecture_graph_revision`、`static_context_snapshot_revision → architecture_static_context_snapshot_revision`、`comparison_status → architecture_comparison_status`。不得凭记忆填写或改写这些值。
177
+
173
178
  `api_gaps_count` 表示前端方案发现但 `api-contract.md` 未覆盖的接口缺口数量。若大于 0,必须由 `workflow-api-supplement` 处理并回写 API 契约后,才能进入 `integration-verify`。
174
179
 
175
180
  ## 注意事项
@@ -204,6 +204,10 @@ Before accepting the rebuttal or retracting the finding, re-open the cited raw s
204
204
 
205
205
  ## Quality Bar
206
206
 
207
+ ## Governance Graph Candidate
208
+
209
+ Submit `risk` and `decision` candidates through `devflow_requirement_graph`, and add missing `behavior` candidates only when backed by a cited source. A `pass` review cannot promote candidates, clear stale nodes, or claim machine validation; it supplies structured candidates and evidence for the Core gate and human checkpoint.
210
+
207
211
  The review must make it easy to trace every conclusion back to evidence. Include:
208
212
 
209
213
  - flow inventory and critical capability chains;
@@ -130,6 +130,10 @@ Accepted
130
130
 
131
131
  ## Meta Output
132
132
 
133
+ ## Governance Graph Candidate
134
+
135
+ Submit bounded candidates through `devflow_requirement_graph`: use `term` for shared language and `behavior` for actor, precondition, state transition, invariant, writes, side effects, failure, and recovery. Link candidates only with returned IDs. Never directly edit the canonical graph or mark a node confirmed; unresolved ownership remains a candidate and is reported in metadata.
136
+
133
137
  `领域模型记录.meta.json` minimum fields:
134
138
 
135
139
  ```json
@@ -727,6 +727,10 @@ Completion gate: baseline and baseline.meta.json are written and contain no unre
727
727
 
728
728
  ### 7.4 产物完整性检查
729
729
 
730
+ ### 7.4.1 Governance Graph Candidate
731
+
732
+ Before claiming baseline readiness, submit `requirement`、`rule`、`boundary`、`slice` and `acceptance` candidates through `devflow_requirement_graph`. Each requirement candidate must cite source IDs and reference behavior/acceptance IDs returned by the writer. Run `devflow_requirement_graph(action="validate")` and record its result in baseline metadata. Do not directly create `governance.json`, allocate IDs, or set candidate status to `confirmed`.
733
+
730
734
  ```markdown
731
735
  - [ ] 每个原始需求映射到 FP-*/BR-*/BC-* 或 out-of-scope
732
736
  - [ ] 追溯表完整,来源→FP 映射可验证
@@ -191,6 +191,10 @@ Ask at most one question, only when route cannot be decided without it. If the n
191
191
 
192
192
  ## Completion Rule
193
193
 
194
+ ## Governance Graph Candidate
195
+
196
+ Before completion, inspect `devflow_requirement_graph` for the current feature. Submit only `source` or `code` candidate deltas for source material and repository facts discovered here. Read the returned graph revision after every accepted delta. Do not invent stable IDs, set a candidate to `confirmed`, or write `governance.json` directly.
197
+
194
198
  Only tell `workflow-orchestrator` to complete `requirement-triage` after writing:
195
199
 
196
200
  ```json
package/src/cli.ts CHANGED
@@ -14,7 +14,7 @@ function usage(): string {
14
14
  " devflow-pi init [project]",
15
15
  " devflow-pi doctor [project]",
16
16
  " devflow-pi usage [project]",
17
- " devflow-pi [project] [--resume [session-id]] [--prompt <text>] [--mcp]",
17
+ " devflow-pi [project] [--resume [session-id]] [--prompt <text>] [--mcp] [--yolo]",
18
18
  ].join("\n");
19
19
  }
20
20
 
@@ -76,6 +76,7 @@ async function main(): Promise<void> {
76
76
  originalOmpAgentDir,
77
77
  resume: optionalOption(args, "--resume"),
78
78
  enableMCP: args.includes("--mcp"),
79
+ approvalMode: args.includes("--yolo") ? "yolo" : "write",
79
80
  });
80
81
  }
81
82
 
package/src/runtime.ts CHANGED
@@ -13,6 +13,7 @@ export interface RuntimeOptions {
13
13
  hasUI?: boolean;
14
14
  enableMCP?: boolean;
15
15
  enableLsp?: boolean;
16
+ approvalMode?: "write" | "yolo";
16
17
  }
17
18
 
18
19
  const MANAGED_SETTINGS: Record<string, unknown> = {
@@ -54,7 +55,6 @@ const MANAGED_SETTINGS: Record<string, unknown> = {
54
55
  "lsp.enabled": true,
55
56
  "lsp.lazy": true,
56
57
  "lsp.diagnosticsOnWrite": true,
57
- "tools.approvalMode": "write",
58
58
  };
59
59
 
60
60
  export async function createEmbeddedRuntime(project: ProjectContext, options: RuntimeOptions = {}) {
@@ -92,6 +92,7 @@ export async function createEmbeddedRuntime(project: ProjectContext, options: Ru
92
92
  agentDir: productAgentDir,
93
93
  overrides: {
94
94
  ...MANAGED_SETTINGS,
95
+ "tools.approvalMode": options.approvalMode ?? "write",
95
96
  // OMP 17.2.7 classifies extension-package skills behind the generic
96
97
  // third-party toggle. Explicit custom roots keep ambient providers off
97
98
  // while guaranteeing the packaged Devflow skills are present.