@guanlin4924/devflow-pi 0.1.92 → 0.1.94

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 (156) hide show
  1. package/README.md +6 -6
  2. package/package.json +1 -1
  3. package/plugins/devflow-core/extensions/devflow-core.ts +87 -33
  4. package/plugins/devflow-core/runtime/hooks/context_sync_gate.py +3 -3
  5. package/plugins/devflow-core/runtime/hooks/hook_common.py +65 -11
  6. package/plugins/devflow-core/runtime/hooks/start.py +13 -13
  7. package/plugins/devflow-core/skills/wireframe-proposal/SKILL.md +2 -2
  8. package/plugins/devflow-core/skills/wireframe-validate/SKILL.md +4 -4
  9. package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +19 -19
  10. package/plugins/devflow-core/skills/workflow-orchestrator/agents/openai.yaml +2 -2
  11. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/acceptance_test_runner.py +5 -5
  12. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +9 -9
  13. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/api_test_matrix.py +2 -2
  14. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_assessment.rb +1 -1
  15. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_brief.rb +1 -1
  16. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/architecture_feasibility.rb +1 -1
  17. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_invalidation.rb +5 -5
  18. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_commit.rb +4 -4
  19. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_validate.py +1 -1
  20. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_source_resolver.rb +4 -4
  21. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/checker_adapter.rb +7 -7
  22. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/control_io.rb +51 -7
  23. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/cross_track_consistency_check.rb +1 -1
  24. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +6 -6
  25. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_runtime.rb +3 -3
  26. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_state.rb +5 -5
  27. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/init_workflow.rb +5 -5
  28. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/inspect_state.rb +13 -13
  29. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/machine_evidence.rb +3 -3
  30. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/manage_lessons.rb +7 -7
  31. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_recover.rb +2 -2
  32. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_run.rb +3 -3
  33. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/phase_ownership.rb +6 -6
  34. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/repair_control_plane.rb +1 -1
  35. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_coverage.rb +1 -1
  36. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +3 -3
  37. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/review_preflight.py +3 -3
  38. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/review_run.py +18 -18
  39. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/runtime_context_collector.rb +3 -3
  40. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/session_rollover.rb +4 -4
  41. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/static_runtime_context_collector.rb +2 -2
  42. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +5 -5
  43. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/tdd_evidence.py +3 -3
  44. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_command_policy.py +2 -2
  45. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/test_task_binding_check.py +3 -3
  46. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/update_triage_route.rb +3 -3
  47. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +6 -6
  48. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_commit.rb +5 -5
  49. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_validate.py +1 -1
  50. package/plugins/devflow-core/skills/workflow-orchestrator/templates/acceptance-remediation.yaml +12 -12
  51. package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +205 -205
  52. package/plugins/devflow-core/skills/workflow-pipeline/SKILL.md +3 -3
  53. package/plugins/devflow-core/skills/workflow-pipeline/references/pipeline-flow.md +10 -10
  54. package/plugins/devflow-core/skills/workflow-project-context/SKILL.md +14 -14
  55. package/plugins/devflow-core/skills/workflow-project-context/assets/agents-md-template.md +7 -7
  56. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-backend-spec-template.md +4 -4
  57. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-frontend-spec-template.md +4 -4
  58. package/plugins/devflow-core/skills/workflow-project-context/assets/codebase-navigation-template.md +1 -1
  59. package/plugins/devflow-core/skills/workflow-project-context/assets/context-domain-index-template.md +4 -4
  60. package/plugins/devflow-core/skills/workflow-project-context/assets/context-spec-index-template.md +10 -10
  61. package/plugins/devflow-core/skills/workflow-project-context/assets/execution-contract-template.md +2 -2
  62. package/plugins/devflow-core/skills/workflow-project-context/assets/navigation-index-template.md +18 -18
  63. package/plugins/devflow-core/skills/workflow-project-context/assets/project-initialization-template.md +32 -32
  64. package/plugins/devflow-core/skills/workflow-project-context/assets/rules-index-template.md +30 -30
  65. package/plugins/devflow-core/skills/workflow-project-context/assets/state-template.md +7 -7
  66. package/plugins/devflow-core/skills/workflow-project-context/references/navigation-guide.md +37 -37
  67. package/plugins/devflow-core/skills/workflow-project-context/scripts/validate_initialization.py +22 -17
  68. package/plugins/devflow-design/skills/workflow-api-call-guide/SKILL.md +4 -4
  69. package/plugins/devflow-design/skills/workflow-api-contract/SKILL.md +8 -8
  70. package/plugins/devflow-design/skills/workflow-api-supplement/SKILL.md +5 -5
  71. package/plugins/devflow-design/skills/workflow-architecture-feasibility/SKILL.md +1 -1
  72. package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +39 -39
  73. package/plugins/devflow-design/skills/workflow-backend-tech-spec/assets/backend-spec-template.md +10 -10
  74. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/affected-module-exploration.md +2 -2
  75. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/code-explore-guide.md +1 -1
  76. package/plugins/devflow-design/skills/workflow-backend-tech-spec/references/project-rule-contract.md +20 -20
  77. package/plugins/devflow-design/skills/workflow-design-rule-pack/SKILL.md +8 -8
  78. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/index.md +3 -3
  79. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/accessibility.md +2 -2
  80. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/component-library.md +2 -2
  81. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/design-systems.md +1 -1
  82. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/responsive-grid.md +2 -2
  83. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/stack-guidelines.md +6 -6
  84. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/recommendations/ux-patterns.md +2 -2
  85. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tokens/project-tokens.md +1 -1
  86. package/plugins/devflow-design/skills/workflow-design-rule-pack/assets/design/tools/ui-ux-pro-max/SKILL.md +12 -12
  87. package/plugins/devflow-design/skills/workflow-design-rule-pack/scripts/install.py +2 -2
  88. package/plugins/devflow-design/skills/workflow-feature-breakdown/SKILL.md +30 -30
  89. package/plugins/devflow-design/skills/workflow-feature-breakdown/assets/output-template.md +2 -2
  90. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +41 -41
  91. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/assets/frontend-spec-template.md +15 -15
  92. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/affected-module-exploration.md +2 -2
  93. package/plugins/devflow-design/skills/workflow-frontend-tech-spec/references/project-rule-contract.md +25 -25
  94. package/plugins/devflow-design/skills/workflow-integration-verify/SKILL.md +13 -13
  95. package/plugins/devflow-design/skills/workflow-ui-spec/SKILL.md +23 -23
  96. package/plugins/devflow-design/skills/workflow-ui-spec/assets/ui-spec-template.md +3 -3
  97. package/plugins/devflow-implementation/agents/implementation-worker.md +2 -2
  98. package/plugins/devflow-implementation/skills/workflow-bug-fix/SKILL.md +9 -9
  99. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/lesson-format.md +2 -2
  100. package/plugins/devflow-implementation/skills/workflow-bug-fix/references/root-cause-ladder.md +1 -1
  101. package/plugins/devflow-implementation/skills/workflow-change-plan/SKILL.md +17 -17
  102. package/plugins/devflow-implementation/skills/workflow-change-plan/references/code-conventions.md +1 -1
  103. package/plugins/devflow-implementation/skills/workflow-change-plan/references/output-template.md +3 -3
  104. package/plugins/devflow-implementation/skills/workflow-change-plan/references/project-rule-contract.md +16 -16
  105. package/plugins/devflow-implementation/skills/workflow-execute-phase/SKILL.md +38 -38
  106. package/plugins/devflow-implementation/skills/workflow-execute-phase/references/execute-phase.md +1 -1
  107. package/plugins/devflow-intel/agents/project-scout.md +3 -1
  108. package/plugins/devflow-intel/extensions/devflow-intel.ts +20 -19
  109. package/plugins/devflow-intel/skills/code-index-query/SKILL.md +8 -6
  110. package/plugins/devflow-intel/skills/workflow-code-index/SKILL.md +12 -12
  111. package/plugins/devflow-intel/skills/workflow-code-index/references/discovery.md +2 -2
  112. package/plugins/devflow-intel/skills/workflow-code-index/references/query-guide.md +1 -1
  113. package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +8 -8
  114. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/discovery.py +2 -2
  115. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/scanner.py +4 -1
  116. package/plugins/devflow-intel/skills/workflow-code-index/scripts/validators/project_binding_check.py +4 -4
  117. package/plugins/devflow-intel/skills/workflow-context-sync/SKILL.md +14 -14
  118. package/plugins/devflow-intel/skills/workflow-context-sync/scripts/context_sync_cursor.py +2 -2
  119. package/plugins/devflow-intel/skills/workflow-update-navigation/SKILL.md +26 -26
  120. package/plugins/devflow-intel/skills/workflow-update-navigation/references/incremental-update-guide.md +2 -2
  121. package/plugins/devflow-quality/skills/code-review/SKILL.md +13 -13
  122. package/plugins/devflow-quality/skills/code-review/references/spec-checklist.md +1 -1
  123. package/plugins/devflow-quality/skills/workflow-acceptance-test-design/SKILL.md +8 -8
  124. package/plugins/devflow-quality/skills/workflow-gen-test-guide/SKILL.md +6 -6
  125. package/plugins/devflow-quality/skills/workflow-gen-test-guide/legacy/SKILL.md +14 -14
  126. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/SKILL.md +23 -23
  127. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/codebase-discovery.md +9 -9
  128. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/references/project-dsl-contract.md +1 -1
  129. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/compile_dsl.py +2 -2
  130. package/plugins/devflow-quality/skills/workflow-generate-mainflow-cases/scripts/discover_codebases.py +2 -2
  131. package/plugins/devflow-quality/skills/workflow-release-closeout/SKILL.md +15 -15
  132. package/plugins/devflow-quality/skills/workflow-run-acceptance-tests/SKILL.md +5 -5
  133. package/plugins/devflow-requirements/skills/requirement-feasibility-review/SKILL.md +1 -1
  134. package/plugins/devflow-requirements/skills/workflow-demand-clarify/SKILL.md +10 -10
  135. package/plugins/devflow-requirements/skills/workflow-domain-modeling/SKILL.md +25 -25
  136. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +49 -49
  137. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/assets/baseline-template.md +22 -22
  138. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/antipatterns.md +3 -3
  139. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/challenge-review-guide.md +3 -3
  140. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/project-context-guide.md +11 -11
  141. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/questioning.md +1 -1
  142. package/plugins/devflow-requirements/skills/workflow-requirement-baseline/references/requirement-review-checklist.md +4 -4
  143. package/plugins/devflow-requirements/skills/workflow-requirement-triage/SKILL.md +16 -16
  144. package/plugins/devflow-requirements/skills/workflow-wayfinder/SKILL.md +6 -6
  145. package/src/cli.ts +15 -0
  146. package/src/constants.ts +1 -1
  147. package/src/initialization-store.ts +3 -2
  148. package/src/project-context.ts +36 -14
  149. package/src/project-documents.ts +61 -61
  150. package/src/project-initializer.ts +23 -9
  151. package/src/project-layout.ts +217 -0
  152. package/src/runtime.ts +3 -3
  153. package/templates/commands/start.md +4 -4
  154. package/templates/omp/AGENTS.md +1 -1
  155. package/templates/omp/hook-config.json +7 -7
  156. package/templates/omp/index-policy.json +8 -8
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: workflow-orchestrator
3
- description: Use when the user asks to continue, resume, advance, inspect, or route a .planning-based workflow; when the OMP development agent must decide the next workflow skill from project state; or when a checkpoint must be handled before proceeding.
3
+ description: Use when the user asks to continue, resume, advance, inspect, or route a .devflow/planning-based workflow; when the OMP development agent must decide the next workflow skill from project state; or when a checkpoint must be handled before proceeding.
4
4
  ---
5
5
 
6
6
  # Workflow Orchestrator
7
7
 
8
8
  ## Overview
9
9
 
10
- Control-plane entrypoint for project workflows. Reads `.planning/STATE.md`, `.planning/workflows/*.yaml`, and the artifact registry, decides the next action, and preserves checkpoint gates. Pipeline delegates here after parameter parsing and breakpoint detection.
10
+ Control-plane entrypoint for project workflows. Reads `.devflow/planning/STATE.md`, `.devflow/planning/workflows/*.yaml`, and the artifact registry, decides the next action, and preserves checkpoint gates. Pipeline delegates here after parameter parsing and breakpoint detection.
11
11
 
12
12
  This skill is the **single source of truth for all execution-time rules**: gate rules, CP protocol, failure branches, parallel windows, state writeback, lessons injection, and requirement writeback. Pipeline only handles entry (args + breakpoint).
13
13
 
@@ -51,7 +51,7 @@ When `blocked` includes a `recovery` command, run or propose that command before
51
51
 
52
52
  `inspect_state.rb` runs `validate_state.rb` before dispatching normal actions. If validation fails, stop at `action: blocked` and fix the reported state/artifact inconsistency before continuing. Do not treat `status: completed` as authoritative unless `validate_state.rb --json` returns `ok: true`.
53
53
 
54
- When `run_project_context` is returned, do not initialize workflow state manually first. Execute `workflow-project-context` with the user's provided codebase locations and project-init arguments, verify it writes `.planning/PROJECT.md`, `.planning/navigation/INDEX.md`, `.context/spec/index.md`, and `.planning/STATE.md`, then run `inspect_state.rb --json` again and continue from the returned action.
54
+ When `run_project_context` is returned, do not initialize workflow state manually first. Execute `workflow-project-context` with the user's provided codebase locations and project-init arguments, verify it writes `.devflow/planning/PROJECT.md`, `.devflow/planning/navigation/INDEX.md`, `.devflow/context/spec/index.md`, and `.devflow/planning/STATE.md`, then run `inspect_state.rb --json` again and continue from the returned action.
55
55
 
56
56
  ## Automatic Run Loop
57
57
 
@@ -63,7 +63,7 @@ When the invocation contains `<devflow-runtime-directive>`, run the workflow con
63
63
  4. Stop only when the inspector returns `await_user_confirmation`, `blocked`, or `workflow_complete`; also stop on an explicit user interruption or an unrecoverable tool failure.
64
64
  5. At `await_user_confirmation`, present the checkpoint and yield to the user. Resume the same loop only after an explicit confirmation and successful `devflow_transition`.
65
65
 
66
- Do not interpret “automatic” as permission to cross a checkpoint, weaken a gate, retry a stale revision without re-inspection, or continue after a blocked action. If context pressure requires a new turn, preserve project truth in `.planning`, finish required context synchronization, and use `/devflow resume` rather than inventing state from the conversation.
66
+ Do not interpret “automatic” as permission to cross a checkpoint, weaken a gate, retry a stale revision without re-inspection, or continue after a blocked action. If context pressure requires a new turn, preserve project truth in `.devflow/planning`, finish required context synchronization, and use `/devflow resume` rather than inventing state from the conversation.
67
67
 
68
68
  ## Step Execution Protocol
69
69
 
@@ -105,7 +105,7 @@ When the inspector returns `await_user_confirmation`:
105
105
  5. Check artifact registry content_valid status for the listed outputs.
106
106
  6. Use `ask` to present the CP prompt, filling in the `summary_fields` from `.meta.json` data.
107
107
  7. If the user confirms, inspect the current revision and call `devflow_transition(action="confirm-checkpoint", expectedRevision=<revision>)`.
108
- 8. If the user asks for fixes or rejects the checkpoint, keep `.planning/STATE.md` at `status: waiting_checkpoint`, inspect the current revision, call `devflow_transition(action="checkpoint-decision", decision="needs_fix", expectedRevision=<revision>)`, and route to the requested remediation.
108
+ 8. If the user asks for fixes or rejects the checkpoint, keep `.devflow/planning/STATE.md` at `status: waiting_checkpoint`, inspect the current revision, call `devflow_transition(action="checkpoint-decision", decision="needs_fix", expectedRevision=<revision>)`, and route to the requested remediation.
109
109
 
110
110
  CP skip rules:
111
111
  - CP-1, CP-API, CP-2, CP-3, CP-4: **never skip**.
@@ -137,7 +137,7 @@ If `hard_gate_mode` is `core`, a gate failure requires re-execution. In the curr
137
137
 
138
138
  When any later step discovers or introduces a change that alters external behavior, the requirement baseline must be updated first before downstream documents or code are modified.
139
139
 
140
- Must writeback to `.planning/requirements/baseline.md` when:
140
+ Must writeback to `.devflow/planning/requirements/baseline.md` when:
141
141
  - New or changed user-visible behavior, notifications, failure handling, retry policy, scheduling, permissions, API contracts, or data write rules
142
142
  - Missing edge cases, compatibility requirements, historical impacts, or non-functional constraints found during execution
143
143
  - User adds/modifies requirements during tech-spec, change-plan, execution, or verification
@@ -194,7 +194,7 @@ devflow_transition(action="enter-failure-branch", expectedRevision=<revision>)
194
194
  Always obtain `<revision>` from a fresh `devflow_state(action="inspect")`. A stale
195
195
  revision is rejected without changing state, registry, or next-intent files.
196
196
 
197
- Checkpoint confirmation also writes the configured `.planning/.next-intent`.
197
+ Checkpoint confirmation also writes the configured `.devflow/planning/.next-intent`.
198
198
 
199
199
  ## Control-plane recovery
200
200
 
@@ -253,19 +253,19 @@ The `.meta.json` data feeds CP prompts — e.g., CP-1 reads `baseline.meta.json`
253
253
 
254
254
  ## Initialization Rules
255
255
 
256
- This skill must be portable. Do not require manually-created `.planning` control files.
256
+ This skill must be portable. Do not require manually-created `.devflow/planning` control files.
257
257
 
258
- If `.planning/STATE.md` is missing and project context files are also missing, route to `workflow-project-context` instead of asking for `init_state.rb`. If `.planning/STATE.md` is missing but project context files already exist, keep the existing `init_state.rb` recovery path.
258
+ If `.devflow/planning/STATE.md` is missing and project context files are also missing, route to `workflow-project-context` instead of asking for `init_state.rb`. If `.devflow/planning/STATE.md` is missing but project context files already exist, keep the existing `init_state.rb` recovery path.
259
259
 
260
- If `.planning/STATE.md` contains blockers with `type: PENDING_TEMPLATE`, stop normal dispatch and report the missing depth spec template. The blocker must name the codebase, topic, matched path, missing template path, and impact.
260
+ If `.devflow/planning/STATE.md` contains blockers with `type: PENDING_TEMPLATE`, stop normal dispatch and report the missing depth spec template. The blocker must name the codebase, topic, matched path, missing template path, and impact.
261
261
 
262
262
  Use these generators:
263
263
 
264
264
  | Script | Generates |
265
265
  |--------|-----------|
266
- | `init_workflow.rb` | `.planning/workflows/*.yaml` and workflow README from bundled templates. |
267
- | `init_state.rb` | `.planning/STATE.md` YAML front matter. |
268
- | `sync_artifacts.rb` | `.planning/artifacts/index.yaml` from active workflow outputs. |
266
+ | `init_workflow.rb` | `.devflow/planning/workflows/*.yaml` and workflow README from bundled templates. |
267
+ | `init_state.rb` | `.devflow/planning/STATE.md` YAML front matter. |
268
+ | `sync_artifacts.rb` | `.devflow/planning/artifacts/index.yaml` from active workflow outputs. |
269
269
 
270
270
  The generated files may be project-specific instances, but their creation path must remain inside this skill.
271
271
 
@@ -273,26 +273,26 @@ The generated files may be project-specific instances, but their creation path m
273
273
 
274
274
  - Do not modify existing `workflow-*` skill behavior.
275
275
  - Auto-execute only the parallel window returned by the inspector while the runtime loop is active. Use task agents when isolation is safe; otherwise use `serial_fallback_order`.
276
- - Do not infer a missing workflow. If `.planning/STATE.md` has no machine-readable front matter, ask to initialize workflow state.
276
+ - Do not infer a missing workflow. If `.devflow/planning/STATE.md` has no machine-readable front matter, ask to initialize workflow state.
277
277
  - Do not skip CP-1, CP-3, or CP-4. CP-2 can only be skipped if the active workflow config explicitly allows it.
278
- - Do not treat `.planning/pipeline-state.md` as the primary state source when YAML front matter exists in `.planning/STATE.md`.
278
+ - Do not treat `.devflow/planning/pipeline-state.md` as the primary state source when YAML front matter exists in `.devflow/planning/STATE.md`.
279
279
  - Do not advance a step if its `meta_output` `.meta.json` is missing or incomplete.
280
280
 
281
281
  ## Files
282
282
 
283
283
  | File | Purpose |
284
284
  |------|---------|
285
- | `.planning/STATE.md` | Primary runtime state, YAML front matter + auto-generated Markdown body. |
286
- | `.planning/workflows/<current_flow>.yaml` | Active workflow config: step topology, CP definitions, meta_output contracts. |
285
+ | `.devflow/planning/STATE.md` | Primary runtime state, YAML front matter + auto-generated Markdown body. |
286
+ | `.devflow/planning/workflows/<current_flow>.yaml` | Active workflow config: step topology, CP definitions, meta_output contracts. |
287
287
  | `failure_branch.workflow_config` | Optional remediation workflow returned by the inspector. |
288
288
  | active workflow `artifact_registry.source` | Artifact status registry with content validation. |
289
289
  | `scripts/inspect_state.rb` | Deterministic state and next-action inspector. |
290
290
  | `scripts/init_workflow.rb` | Creates required workflow YAML files from bundled templates. |
291
- | `scripts/init_state.rb` | Creates or repairs `.planning/STATE.md` front matter. |
291
+ | `scripts/init_state.rb` | Creates or repairs `.devflow/planning/STATE.md` front matter. |
292
292
  | `scripts/sync_artifacts.rb` | Generates artifact registry with content validation. |
293
293
  | `scripts/validate_state.rb` | Verifies STATE.md, workflow steps, dependencies, outputs, meta outputs, and artifact statuses agree before dispatch or writeback. |
294
294
  | `scripts/advance_state.rb` | State writeback + Markdown body auto-generation. |
295
295
  | `scripts/parallel_recover.rb` | Audited, CAS-guarded recovery and join for legacy lost parallel lifecycle events. |
296
- | `scripts/update_triage_route.rb` | Deterministically updates `.planning/requirements/{feature}/入口分诊.meta.json` route/status after clarify or wayfinder. |
296
+ | `scripts/update_triage_route.rb` | Deterministically updates `.devflow/planning/requirements/{feature}/入口分诊.meta.json` route/status after clarify or wayfinder. |
297
297
  | `scripts/manage_lessons.rb` | Lesson candidate selector and evidence-based template writer. |
298
298
  | `templates/` | Portable workflow templates used by `init_workflow.rb`. |
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Workflow Orchestrator"
3
- short_description: "Route .planning workflows, checkpoints, remediation, and parallel windows"
4
- default_prompt: "Inspect .planning/STATE.md and route the workflow safely"
3
+ short_description: "Route .devflow/planning workflows, checkpoints, remediation, and parallel windows"
4
+ default_prompt: "Inspect .devflow/planning/STATE.md and route the workflow safely"
@@ -134,9 +134,9 @@ def frozen_cases(suite: dict[str, Any], errors: list[str]) -> dict[str, dict[str
134
134
 
135
135
  def paths(root: Path, feature: str) -> tuple[Path, Path, Path]:
136
136
  return (
137
- root / ".planning" / "requirements" / feature / "tests.json",
138
- root / ".planning" / "features" / feature / "acceptance-test-bindings.json",
139
- root / ".planning" / "test-runs" / feature / "acceptance-results.json",
137
+ root / ".devflow/planning" / "requirements" / feature / "tests.json",
138
+ root / ".devflow/planning" / "features" / feature / "acceptance-test-bindings.json",
139
+ root / ".devflow/planning" / "test-runs" / feature / "acceptance-results.json",
140
140
  )
141
141
 
142
142
 
@@ -339,8 +339,8 @@ def run(root: Path, feature: str, write: bool) -> dict[str, Any]:
339
339
  cases = frozen_cases(suite, [])
340
340
  _, _, result_path = paths(root, feature)
341
341
  previous = read_json(result_path, [], "previous acceptance-test results")
342
- task_bindings_path = root / ".planning" / "features" / feature / "test-bindings.json"
343
- execution_bindings_path = root / ".planning" / "features" / feature / "acceptance-test-bindings.json"
342
+ task_bindings_path = root / ".devflow/planning" / "features" / feature / "test-bindings.json"
343
+ execution_bindings_path = root / ".devflow/planning" / "features" / feature / "acceptance-test-bindings.json"
344
344
  results: list[dict[str, Any]] = []
345
345
  for entry in bindings["cases"]:
346
346
  tc_id = entry["tc_id"]
@@ -59,7 +59,7 @@ OptionParser.new do |opts|
59
59
  end.parse!
60
60
 
61
61
  root = Dir.pwd
62
- state_path, = DevflowControlIO.resolve_control_path(root, options[:state] || ".planning/STATE.md", :state)
62
+ state_path, = DevflowControlIO.resolve_control_path(root, options[:state] || ".devflow/planning/STATE.md", :state)
63
63
 
64
64
  def front_matter(markdown)
65
65
  match = markdown.match(/\A---\n(.*?)\n---\n/m)
@@ -71,7 +71,7 @@ def load_yaml_file(path)
71
71
  end
72
72
 
73
73
  def workflow_path_for(root, state)
74
- state["workflow_config"] || File.join(".planning", "workflows", "#{state.fetch("current_flow")}.yaml")
74
+ state["workflow_config"] || File.join(".devflow/planning", "workflows", "#{state.fetch("current_flow")}.yaml")
75
75
  end
76
76
 
77
77
  def load_workflow(root, state)
@@ -384,7 +384,7 @@ def checkpoint_label(workflow, checkpoint_id)
384
384
  end
385
385
 
386
386
  def next_intent_path(root, workflow)
387
- workflow.dig("resume_rules", "continue_keyword", "next_intent_path") || ".planning/.next-intent"
387
+ workflow.dig("resume_rules", "continue_keyword", "next_intent_path") || ".devflow/planning/.next-intent"
388
388
  end
389
389
 
390
390
  def write_next_intent(root, workflow)
@@ -635,16 +635,16 @@ end
635
635
 
636
636
  def business_cross_model_check(root, feature)
637
637
  script = File.join(__dir__, "business_cross_model_check.py")
638
- business = ".planning/business-model/#{feature}/canonical.json"
639
- wireframe = ".planning/features/#{feature}/canonical.json"
638
+ business = ".devflow/planning/business-model/#{feature}/canonical.json"
639
+ wireframe = ".devflow/planning/features/#{feature}/canonical.json"
640
640
  unless File.file?(File.join(root, business))
641
641
  return { "valid" => false, "errors" => [{ "code" => "BUSINESS_CANONICAL_MISSING", "path" => business }] }
642
642
  end
643
643
  command = ["python3", script, "--business-canonical", business]
644
644
  command += ["--wireframe", wireframe] if File.file?(File.join(root, wireframe))
645
- claims = ".planning/features/#{feature}/business-claims.yaml"
645
+ claims = ".devflow/planning/features/#{feature}/business-claims.yaml"
646
646
  command += ["--claims", claims] if File.file?(File.join(root, claims))
647
- traceability = ".planning/requirements/#{feature}/traceability.json"
647
+ traceability = ".devflow/planning/requirements/#{feature}/traceability.json"
648
648
  command += ["--traceability", traceability] if File.file?(File.join(root, traceability))
649
649
  stdout, stderr, status = Open3.capture3(*command, "--json", chdir: root)
650
650
  payload = JSON.parse(stdout)
@@ -675,7 +675,7 @@ rescue JSON::ParserError => error
675
675
  end
676
676
 
677
677
  def test_suite_freeze_attestation_path(root, feature)
678
- DevflowControlIO.resolve_control_path(root, ".planning/features/#{feature}/test-suite-freeze.json", :artifact)
678
+ DevflowControlIO.resolve_control_path(root, ".devflow/planning/features/#{feature}/test-suite-freeze.json", :artifact)
679
679
  end
680
680
 
681
681
  def frozen_test_suite_pin(freeze_result)
@@ -1218,7 +1218,7 @@ result =
1218
1218
  prospective_owner = if changed
1219
1219
  {
1220
1220
  "phase" => state["current_phase"], "feature" => state["current_feature"],
1221
- "flow" => state["current_flow"], "state_path" => ".planning/STATE.md",
1221
+ "flow" => state["current_flow"], "state_path" => ".devflow/planning/STATE.md",
1222
1222
  "scope" => "global", "status" => "active"
1223
1223
  }
1224
1224
  end
@@ -72,8 +72,8 @@ def write_text(path: Path, value: str) -> None:
72
72
 
73
73
 
74
74
  def paths(root: Path, feature: str) -> tuple[Path, Path, Path, Path]:
75
- requirements = root / ".planning" / "requirements" / feature
76
- feature_dir = root / ".planning" / "features" / feature
75
+ requirements = root / ".devflow/planning" / "requirements" / feature
76
+ feature_dir = root / ".devflow/planning" / "features" / feature
77
77
  return (
78
78
  requirements / "governance.json",
79
79
  requirements / "api-test-matrix.json",
@@ -17,7 +17,7 @@ require_relative "runtime_context_collector"
17
17
  module ArchitectureAssessment
18
18
  SCHEMA_VERSION = 1
19
19
  RULE_VERSION = 1
20
- ROOT_DIR = File.join(".planning", "architecture-assessment")
20
+ ROOT_DIR = File.join(".devflow/planning", "architecture-assessment")
21
21
 
22
22
  UNRESOLVED = {
23
23
  DELTA_SET_BINDING_FAILED: "DELTA_SET_BINDING_FAILED",
@@ -12,7 +12,7 @@ require_relative "architecture_feasibility"
12
12
  require_relative "control_io"
13
13
 
14
14
  module ArchitectureBrief
15
- ROOT = ".planning/features"
15
+ ROOT = ".devflow/planning/features"
16
16
  SCHEMA_VERSION = 1
17
17
 
18
18
  module_function
@@ -27,7 +27,7 @@ module ArchitectureFeasibility
27
27
 
28
28
  pointer = JSON.parse(File.read(pointer_path))
29
29
  relative = pointer.fetch("path")
30
- raise ArgumentError, "static runtime-context pointer path is invalid" unless relative.is_a?(String) && relative.start_with?(".context/static-runtime-context/revisions/")
30
+ raise ArgumentError, "static runtime-context pointer path is invalid" unless relative.is_a?(String) && relative.start_with?(".devflow/context/static-runtime-context/revisions/")
31
31
 
32
32
  absolute = File.expand_path(relative, root)
33
33
  root_real = File.realpath(root)
@@ -11,7 +11,7 @@ require "time"
11
11
  require "yaml"
12
12
  require_relative "control_io"
13
13
 
14
- options = { root: ".", feature: nil, canonical: nil, changed_source: [], registry: ".planning/artifacts/index.yaml", write: false, json: false }
14
+ options = { root: ".", feature: nil, canonical: nil, changed_source: [], registry: ".devflow/planning/artifacts/index.yaml", write: false, json: false }
15
15
  OptionParser.new do |o|
16
16
  o.on("--root PATH") { |v| options[:root] = v }
17
17
  o.on("--feature NAME") { |v| options[:feature] = v }
@@ -42,7 +42,7 @@ lock = nil
42
42
  begin
43
43
  raise ArgumentError, "feature is required" if options[:feature].to_s.empty?
44
44
  raise ArgumentError, "at least one changed source is required" if options[:changed_source].empty?
45
- canonical_path = options[:canonical] || ".planning/business-model/#{options[:feature]}/canonical.json"
45
+ canonical_path = options[:canonical] || ".devflow/planning/business-model/#{options[:feature]}/canonical.json"
46
46
  canonical_absolute, canonical_relative = DevflowControlIO.resolve_control_path(root, canonical_path, :artifact)
47
47
  model = read_json(canonical_absolute)
48
48
  changed = options[:changed_source].uniq
@@ -56,7 +56,7 @@ begin
56
56
  end
57
57
  end
58
58
  affected_ids = affected.map { |item| item["id"].to_s }.to_set
59
- wireframe_relative = ".planning/features/#{options[:feature]}/canonical.json"
59
+ wireframe_relative = ".devflow/planning/features/#{options[:feature]}/canonical.json"
60
60
  wireframe_path, = DevflowControlIO.resolve_control_path(root, wireframe_relative, :artifact)
61
61
  consumers = []
62
62
  if File.file?(wireframe_path)
@@ -70,7 +70,7 @@ begin
70
70
  if options[:write]
71
71
  lock = DevflowControlIO.acquire_project_lock(root)
72
72
  timestamp = Time.now.utc.iso8601
73
- approval_relative = ".planning/features/#{options[:feature]}/approval.json"
73
+ approval_relative = ".devflow/planning/features/#{options[:feature]}/approval.json"
74
74
  approval_path, = DevflowControlIO.resolve_control_path(root, approval_relative, :artifact)
75
75
  if !consumers.empty? && File.file?(approval_path)
76
76
  approval = read_json(approval_path)
@@ -93,7 +93,7 @@ begin
93
93
  DevflowControlIO.atomic_write(registry_path, registry.to_yaml.sub(/\A---\n/, ""))
94
94
  report["registry_path"] = registry_relative
95
95
  end
96
- report_path = File.join(root, ".planning", "business-model", options[:feature], "invalidation.json")
96
+ report_path = File.join(root, ".devflow/planning", "business-model", options[:feature], "invalidation.json")
97
97
  DevflowControlIO.atomic_write(report_path, JSON.pretty_generate(report) + "\n")
98
98
  report["written"] = true
99
99
  else
@@ -43,7 +43,7 @@ module BusinessModelCommit
43
43
  raise ArgumentError, "invalid feature" unless valid_feature?(feature)
44
44
 
45
45
  root_real = File.realpath(root)
46
- relative = File.join(".planning", "business-model", feature)
46
+ relative = File.join(".devflow/planning", "business-model", feature)
47
47
  absolute = File.expand_path(relative, root_real)
48
48
  raise ArgumentError, "business model path escapes project root" unless absolute.start_with?("#{root_real}#{File::SEPARATOR}")
49
49
 
@@ -67,7 +67,7 @@ module BusinessModelCommit
67
67
  raise ArgumentError, "proposal path contains control characters" if value.match?(/[\x00-\x1F\x7F]/)
68
68
 
69
69
  normalized = Pathname.new(value).cleanpath.to_s.tr("\\", "/")
70
- prefix = File.join(".planning", "business-model", feature, "proposals").tr("\\", "/") + "/"
70
+ prefix = File.join(".devflow/planning", "business-model", feature, "proposals").tr("\\", "/") + "/"
71
71
  raise ArgumentError, "proposal path is outside the feature proposal directory" unless normalized.start_with?(prefix)
72
72
  absolute = File.expand_path(normalized, root_real)
73
73
  raise ArgumentError, "proposal path escapes project root" unless absolute.start_with?("#{root_real}#{File::SEPARATOR}")
@@ -255,7 +255,7 @@ module BusinessModelCommit
255
255
  def load_delta(root, feature, raw)
256
256
  raise ArgumentError, "candidate delta path is required" if raw.to_s.empty?
257
257
  path = File.expand_path(raw, root)
258
- feature_root = File.expand_path(File.join(".planning", "business-model", feature, "candidates"), root)
258
+ feature_root = File.expand_path(File.join(".devflow/planning", "business-model", feature, "candidates"), root)
259
259
  raise ArgumentError, "candidate delta is missing" unless File.file?(path)
260
260
  path = File.realpath(path)
261
261
  feature_root = File.realpath(feature_root)
@@ -290,7 +290,7 @@ module BusinessModelCommit
290
290
  def source_registry_fingerprint(root, feature, aggregate)
291
291
  registry_ref = aggregate.dig("merged", "source_registry_ref").to_s
292
292
  return "" if registry_ref.empty?
293
- path = if registry_ref.start_with?(".planning/")
293
+ path = if registry_ref.start_with?(".devflow/planning/")
294
294
  File.expand_path(registry_ref, root)
295
295
  else
296
296
  File.expand_path(registry_ref, feature_dir(root, feature))
@@ -606,7 +606,7 @@ def load_json(path: Path) -> Any:
606
606
 
607
607
 
608
608
  def requirement_nodes(root: Path, feature: str, violations: list[dict[str, Any]]) -> set[str]:
609
- requirements_root = (root / ".planning" / "requirements").resolve()
609
+ requirements_root = (root / ".devflow/planning" / "requirements").resolve()
610
610
  path = (requirements_root / feature / "governance.json").resolve()
611
611
  if not path.is_relative_to(requirements_root):
612
612
  add_violation(violations, "BUSINESS_REQUIREMENT_GRAPH_INVALID", feature, path=str(path), message="feature escapes requirements directory")
@@ -101,7 +101,7 @@ module BusinessSourceResolver
101
101
  output = command(root, "git", "status", "--porcelain=v1", "--untracked-files=all")
102
102
  lines = output.lines.map(&:rstrip).reject do |line|
103
103
  path = line.length > 3 ? line[3..].to_s.strip : ""
104
- path.start_with?(".planning/", ".context/")
104
+ path.start_with?(".devflow/planning/", ".devflow/context/")
105
105
  end
106
106
  "sha256:#{Digest::SHA256.hexdigest(lines.join("\n"))}"
107
107
  end
@@ -237,7 +237,7 @@ module BusinessSourceResolver
237
237
  current_signature = worktree_signature(root)
238
238
  add(errors, "BUSINESS_WORKTREE_SIGNATURE_MISMATCH", "worktree signature is stale", source_id: id) unless source["worktree_signature"].to_s == current_signature
239
239
 
240
- snapshot_path = File.join(root, ".planning", "code-index", "SNAPSHOT.json")
240
+ snapshot_path = File.join(root, ".devflow/planning", "code-index", "SNAPSHOT.json")
241
241
  unless File.file?(snapshot_path)
242
242
  add(errors, "BUSINESS_CODE_INDEX_STALE", "code index snapshot is missing", source_id: id, path: snapshot_path)
243
243
  return
@@ -258,7 +258,7 @@ module BusinessSourceResolver
258
258
  end
259
259
  return if symbol.empty?
260
260
 
261
- symbols_path = File.join(root, ".planning", "code-index", "symbols.jsonl")
261
+ symbols_path = File.join(root, ".devflow/planning", "code-index", "symbols.jsonl")
262
262
  unless File.file?(symbols_path)
263
263
  add(errors, "BUSINESS_CODE_SYMBOL_NOT_FOUND", "symbols index is missing", source_id: id, path: symbols_path)
264
264
  return
@@ -318,7 +318,7 @@ module BusinessSourceResolver
318
318
  raise ArgumentError, "invalid feature" unless valid_feature?(feature)
319
319
  root = File.realpath(root)
320
320
  schema = load_yaml(REGISTRY_SCHEMA)
321
- raw_registry = registry_path || File.join(".planning", "business-model", feature, "source-registry.yaml")
321
+ raw_registry = registry_path || File.join(".devflow/planning", "business-model", feature, "source-registry.yaml")
322
322
  registry_absolute, registry_relative = safe_path(root, raw_registry, "source registry")
323
323
  registry = load_yaml(registry_absolute)
324
324
  errors = []
@@ -60,22 +60,22 @@ module CheckerAdapter
60
60
  when "cross-track-consistency-check"
61
61
  ["ruby", File.join(scripts, "cross_track_consistency_check.rb"), "--feature", feature, "--json"]
62
62
  when "business-cross-model-check"
63
- business = ".planning/business-model/#{feature}/canonical.json"
63
+ business = ".devflow/planning/business-model/#{feature}/canonical.json"
64
64
  return nil unless File.file?(File.join(root, business))
65
65
  command = ["python3", File.join(scripts, "business_cross_model_check.py"), "--business-canonical", business]
66
- wireframe = ".planning/features/#{feature}/canonical.json"
67
- claims = ".planning/features/#{feature}/business-claims.yaml"
68
- traceability = ".planning/requirements/#{feature}/traceability.json"
66
+ wireframe = ".devflow/planning/features/#{feature}/canonical.json"
67
+ claims = ".devflow/planning/features/#{feature}/business-claims.yaml"
68
+ traceability = ".devflow/planning/requirements/#{feature}/traceability.json"
69
69
  command += ["--wireframe", wireframe] if File.file?(File.join(root, wireframe))
70
70
  command += ["--claims", claims] if File.file?(File.join(root, claims))
71
71
  command += ["--traceability", traceability] if File.file?(File.join(root, traceability))
72
72
  command + ["--json"]
73
73
  when "business-claim-reference-check"
74
- claims = ".planning/features/#{feature}/business-claims.yaml"
75
- business = ".planning/business-model/#{feature}/canonical.json"
74
+ claims = ".devflow/planning/features/#{feature}/business-claims.yaml"
75
+ business = ".devflow/planning/business-model/#{feature}/canonical.json"
76
76
  return nil unless File.file?(File.join(root, claims)) && File.file?(File.join(root, business))
77
77
  command = ["python3", File.join(scripts, "business_claim_validate.py"), "--claims", claims, "--business-canonical", business]
78
- traceability = ".planning/requirements/#{feature}/traceability.json"
78
+ traceability = ".devflow/planning/requirements/#{feature}/traceability.json"
79
79
  command += ["--traceability", traceability] if File.file?(File.join(root, traceability))
80
80
  command
81
81
  end
@@ -5,6 +5,7 @@ require "fileutils"
5
5
  require "json"
6
6
  require "pathname"
7
7
  require "securerandom"
8
+ require "time"
8
9
 
9
10
  # Shared durability primitives for every writer in the Devflow control plane.
10
11
  # The project lock serializes state, workflow, registry, and next-intent updates;
@@ -14,10 +15,52 @@ module DevflowControlIO
14
15
 
15
16
  module_function
16
17
 
18
+ def assert_layout_ready(root)
19
+ root_path = File.realpath(root)
20
+ legacy_planning = File.join(root_path, ".planning")
21
+ legacy_context = File.join(root_path, ".context")
22
+ planning_markers = %w[STATE.md PROJECT.md .devflow.lock navigation/INDEX.md code-index/INDEX.md workflows contracts evidence review-runs requirements features handoffs artifacts phase-ownership.yaml]
23
+ context_markers = %w[spec/index.md index.md project/index.md knowledge/index.md]
24
+ legacy_markers = planning_markers.map { |item| File.join(legacy_planning, item) } + context_markers.map { |item| File.join(legacy_context, item) }
25
+ return true unless legacy_markers.any? { |item| File.exist?(item) }
26
+
27
+ manifest = JSON.parse(File.read(File.join(root_path, ".devflow", "project.json")))
28
+ valid_manifest = manifest["schemaVersion"] == 1 &&
29
+ manifest["projectId"].to_s.match?(/\A[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/i) &&
30
+ !manifest["name"].to_s.strip.empty? &&
31
+ !Time.iso8601(manifest["createdAt"].to_s).nil? &&
32
+ [nil, 1, 2].include?(manifest["layoutVersion"])
33
+ raise InvalidControlPath, "DEVFLOW_LEGACY_LAYOUT_CONFIRMATION_REQUIRED: valid project manifest required" unless valid_manifest
34
+
35
+ marker = JSON.parse(File.read(File.join(root_path, ".devflow", "layout-migration.json"))) rescue nil
36
+ migration_complete = marker.is_a?(Hash) && marker["schemaVersion"] == 1 && marker["status"] == "complete" && marker["migrated"].is_a?(Array)
37
+ active = %w[STATE.md workflows contracts evidence review-runs requirements features handoffs artifacts phase-ownership.yaml].find do |item|
38
+ File.exist?(File.join(legacy_planning, item))
39
+ end
40
+ raise InvalidControlPath, "DEVFLOW_ACTIVE_LEGACY_LAYOUT: .planning/#{active} requires dedicated control-plane migration" if active && !migration_complete
41
+ canonical_exists = File.exist?(File.join(root_path, ".devflow", "planning")) || File.exist?(File.join(root_path, ".devflow", "context"))
42
+ if manifest["layoutVersion"] == 2 && !migration_complete
43
+ raise InvalidControlPath, "DEVFLOW_LEGACY_REINTRODUCTION: v2 project cannot import legacy layout"
44
+ end
45
+ if manifest["layoutVersion"] == 2 && migration_complete
46
+ planning_legacy = planning_markers.any? { |item| File.exist?(File.join(legacy_planning, item)) }
47
+ context_legacy = context_markers.any? { |item| File.exist?(File.join(legacy_context, item)) }
48
+ missing_canonical = (planning_legacy && !File.exist?(File.join(root_path, ".devflow", "planning"))) ||
49
+ (context_legacy && !File.exist?(File.join(root_path, ".devflow", "context")))
50
+ raise InvalidControlPath, "DEVFLOW_LEGACY_REINTRODUCTION: v2 project will not backfill a missing canonical directory" if missing_canonical
51
+ end
52
+ raise InvalidControlPath, "DEVFLOW_LAYOUT_CONFLICT: canonical and legacy layouts coexist without migration marker" if canonical_exists && !migration_complete
53
+ raise InvalidControlPath, "DEVFLOW_LAYOUT_MIGRATION_REQUIRED: run project initialization" unless canonical_exists
54
+ true
55
+ rescue Errno::ENOENT, JSON::ParserError, ArgumentError
56
+ raise InvalidControlPath, "DEVFLOW_LEGACY_LAYOUT_CONFIRMATION_REQUIRED: valid project manifest required"
57
+ end
58
+
17
59
  # Control-plane files are not general-purpose CLI file arguments. Keeping
18
60
  # their location fixed prevents a crafted STATE/workflow/registry reference
19
61
  # from turning recovery or synchronization into an out-of-project writer.
20
62
  def resolve_control_path(root, raw, kind)
63
+ assert_layout_ready(root)
21
64
  root_path = File.realpath(root)
22
65
  value = raw.to_s
23
66
  raise InvalidControlPath, "#{kind} path is required" if value.empty?
@@ -27,14 +70,14 @@ module DevflowControlIO
27
70
  normalized = Pathname.new(value).cleanpath.to_s.tr("\\", "/")
28
71
  raise InvalidControlPath, "#{kind} path cannot traverse parent directories" if normalized == ".." || normalized.start_with?("../")
29
72
  allowed = case kind
30
- when :state then normalized == ".planning/STATE.md"
31
- when :workflow then normalized.match?(%r{\A\.planning/workflows/[A-Za-z0-9][A-Za-z0-9_-]*\.yaml\z})
32
- when :registry then normalized == ".planning/artifacts/index.yaml"
33
- when :artifact then normalized.start_with?(".planning/") || normalized.start_with?(".context/")
73
+ when :state then normalized == ".devflow/planning/STATE.md"
74
+ when :workflow then normalized.match?(%r{\A\.devflow/planning/workflows/[A-Za-z0-9][A-Za-z0-9_-]*\.yaml\z})
75
+ when :registry then normalized == ".devflow/planning/artifacts/index.yaml"
76
+ when :artifact then normalized.start_with?(".devflow/planning/") || normalized.start_with?(".devflow/context/")
34
77
  # Feature names are user-facing and may be Unicode. `cleanpath`
35
78
  # and the parent-traversal check above own containment; this
36
79
  # matcher only fixes the handoff namespace and immutable name.
37
- when :handoff then normalized.match?(%r{\A\.planning/handoffs/[^/]+/ROL-[0-9a-f-]+\.json\z})
80
+ when :handoff then normalized.match?(%r{\A\.devflow/planning/handoffs/[^/]+/ROL-[0-9a-f-]+\.json\z})
38
81
  else false
39
82
  end
40
83
  raise InvalidControlPath, "invalid #{kind} control path" unless allowed
@@ -58,7 +101,8 @@ module DevflowControlIO
58
101
  end
59
102
 
60
103
  def acquire_project_lock(root)
61
- planning_dir = File.join(root, ".planning")
104
+ assert_layout_ready(root)
105
+ planning_dir = File.join(root, ".devflow/planning")
62
106
  FileUtils.mkdir_p(planning_dir)
63
107
  handle = File.open(File.join(planning_dir, ".devflow.lock"), File::RDWR | File::CREAT, 0o644)
64
108
  handle.flock(File::LOCK_EX)
@@ -122,7 +166,7 @@ module DevflowControlIO
122
166
  # 独立受控工件登记:与 sync_artifacts 的 workflow-outputs registry 分开,
123
167
  # 避免被 workflow 重建覆盖。按 path 去重,last-write-wins。
124
168
  def register_control_artifact(root, entry)
125
- path = File.join(root, ".planning", "artifacts", "control-artifacts.json")
169
+ path = File.join(root, ".devflow/planning", "artifacts", "control-artifacts.json")
126
170
  existing = File.exist?(path) ? JSON.parse(File.read(path)) : { "schema_version" => 1, "artifacts" => [] }
127
171
  existing["artifacts"] ||= []
128
172
  existing["artifacts"].reject! { |item| item["path"] == entry["path"] }
@@ -16,7 +16,7 @@ feature = options[:feature].to_s
16
16
  raise ArgumentError, "feature contains path separators" if feature.include?("/") || feature.include?("\\")
17
17
 
18
18
  root = Dir.pwd
19
- base = File.join(root, ".planning", "features", feature)
19
+ base = File.join(root, ".devflow/planning", "features", feature)
20
20
  files = {
21
21
  "api_contract" => File.join(base, "api-contract.md"),
22
22
  "api_call_guide" => File.join(base, "api-call-guide.md"),
@@ -43,7 +43,7 @@ OptionParser.new do |o|
43
43
  end.parse!
44
44
 
45
45
  def load_state(root)
46
- document = File.read(File.join(root, ".planning/STATE.md"))
46
+ document = File.read(File.join(root, ".devflow/planning/STATE.md"))
47
47
  front = document.match(/\A---\n(.*?)\n---/m)&.[](1)
48
48
  raise "STATE.md has no YAML front matter" unless front
49
49
  YAML.safe_load(front, permitted_classes: [], aliases: false)
@@ -57,7 +57,7 @@ def render_execution_contract_markdown(contract)
57
57
  lines = [
58
58
  "# Execution Contract",
59
59
  "",
60
- "> Core-generated derived view. The immutable contract JSON under `.planning/contracts/` is authoritative.",
60
+ "> Core-generated derived view. The immutable contract JSON under `.devflow/planning/contracts/` is authoritative.",
61
61
  "",
62
62
  "- Contract ID: `#{contract.fetch("contract_id")}`",
63
63
  "- Feature: `#{contract.fetch("feature")}`",
@@ -106,7 +106,7 @@ begin
106
106
  # omitting production_paths strands implementation workers in a contract
107
107
  # that can write summaries but never code.
108
108
  if options[:step] == "execute"
109
- binding_path = File.join(root, ".planning/features", options[:feature], "test-bindings.json")
109
+ binding_path = File.join(root, ".devflow/planning/features", options[:feature], "test-bindings.json")
110
110
  if File.file?(binding_path)
111
111
  bindings = JSON.parse(File.read(binding_path))
112
112
  Array(bindings["tasks"]).each do |task|
@@ -142,10 +142,10 @@ begin
142
142
  result = { "ok" => true, "contract" => frozen }
143
143
  else
144
144
  raise ArgumentError, "--write is required" unless options[:write]
145
- destination = File.join(root, ".planning/contracts", options[:feature], "#{contract_id}.json")
145
+ destination = File.join(root, ".devflow/planning/contracts", options[:feature], "#{contract_id}.json")
146
146
  DevflowControlIO.atomic_write(destination, JSON.pretty_generate(frozen) + "\n")
147
- DevflowControlIO.atomic_write(File.join(root, ".planning/execution-contract.md"), render_execution_contract_markdown(frozen) + "\n")
148
- audit = File.join(root, ".planning/contracts", options[:feature], "control-events.jsonl")
147
+ DevflowControlIO.atomic_write(File.join(root, ".devflow/planning/execution-contract.md"), render_execution_contract_markdown(frozen) + "\n")
148
+ audit = File.join(root, ".devflow/planning/contracts", options[:feature], "control-events.jsonl")
149
149
  existing = File.exist?(audit) ? File.read(audit) : ""
150
150
  DevflowControlIO.atomic_write(audit, existing + JSON.generate({ "event" => "execution_contract_frozen", "contract_id" => contract_id, "state_revision" => state["revision"], "graph_revision" => graph["revision"], "frozen_hash" => frozen["frozen_hash"] }) + "\n")
151
151
  result = { "ok" => true, "path" => destination.sub("#{root}/", ""), "contract" => frozen }
@@ -87,7 +87,7 @@ module GateRuntime
87
87
  invalid_outputs = validate_declared_inputs(contract.dig("outputs", "required_inputs"), root, workflow_state, purpose: :completion)
88
88
  return GateResult.block(**base, code: "REQUIRED_OUTPUT_INVALID", message: "required step input/output fact is invalid", details: { "invalid" => invalid_outputs }) unless invalid_outputs.empty?
89
89
  if step_id == "code-review"
90
- contract_view = File.join(root, ".planning/execution-contract.md")
90
+ contract_view = File.join(root, ".devflow/planning/execution-contract.md")
91
91
  contract_id = if File.file?(contract_view) && !File.symlink?(contract_view)
92
92
  File.read(contract_view).match(/^- Contract ID: `(EC-[0-9a-f]{16})`$/)&.[](1)
93
93
  end
@@ -234,9 +234,9 @@ module GateRuntime
234
234
  case validation
235
235
  when "static-runtime-context"
236
236
  pointer_path = payload.fetch("path")
237
- raise "invalid pointer path" unless pointer_path.start_with?(".context/static-runtime-context/revisions/")
237
+ raise "invalid pointer path" unless pointer_path.start_with?(".devflow/context/static-runtime-context/revisions/")
238
238
  root_real = File.realpath(root)
239
- revisions_root = File.realpath(File.join(root, ".context/static-runtime-context/revisions"))
239
+ revisions_root = File.realpath(File.join(root, ".devflow/context/static-runtime-context/revisions"))
240
240
  snapshot_real = File.realpath(File.expand_path(pointer_path, root))
241
241
  raise "snapshot path escapes project root" unless snapshot_real.start_with?("#{root_real}#{File::SEPARATOR}")
242
242
  raise "snapshot path escapes revisions directory" unless snapshot_real.start_with?("#{revisions_root}#{File::SEPARATOR}")