@khoavu882/doflow 2.0.0-beta.4 → 2.0.0

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 (497) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  3. package/core/.antigravity-plugin/plugin.json +1 -3
  4. package/core/.claude-plugin/plugin.json +1 -1
  5. package/core/.codex-plugin/plugin.json +1 -1
  6. package/core/.plugin/plugin.json +1 -1
  7. package/core/harnesses/claude/hooks/lib.sh +43 -24
  8. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  9. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  10. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  11. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  12. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  13. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  14. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  15. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  16. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  17. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  18. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  19. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  20. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  21. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  22. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  23. package/core/harnesses/claude/settings/settings.json +18 -0
  24. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  25. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  26. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  27. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  28. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  29. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  30. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  31. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  32. package/core/harnesses/codex/hooks/lib.sh +44 -25
  33. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  34. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  35. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  36. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  37. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  38. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  39. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  40. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  41. package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
  42. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  43. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  44. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  45. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  46. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  47. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  48. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  49. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  50. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  51. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  52. package/core/harnesses/kiro/agents/doflow.json +1 -1
  53. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  54. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
  55. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
  56. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
  57. package/core/harnesses/kiro/hooks/session-start.sh +12 -5
  58. package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
  59. package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
  60. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
  61. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  62. package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
  63. package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
  64. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  65. package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
  66. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  67. package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
  68. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  69. package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
  70. package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
  71. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  72. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  73. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  74. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  75. package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
  76. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
  77. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
  78. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
  79. package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
  80. package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
  81. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
  82. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  83. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
  84. package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
  85. package/core/harnesses/shared/locator/doflow-run +2 -3
  86. package/core/registry/advice.json +3 -3
  87. package/core/registry/assets.json +72 -80
  88. package/core/registry/capabilities.json +12 -0
  89. package/core/registry/contracts.json +60 -33
  90. package/core/registry/contribution-points.json +32 -0
  91. package/core/registry/gates.json +32 -0
  92. package/core/registry/guidance-caps.json +40 -0
  93. package/core/registry/harnesses.json +1542 -277
  94. package/core/registry/lifecycle.json +2837 -37
  95. package/core/registry/mcp.json +5 -1
  96. package/core/registry/models.json +7 -1
  97. package/core/registry/package-templates/README.md +19 -0
  98. package/core/registry/package-templates/agent/files/agent.md +9 -0
  99. package/core/registry/package-templates/agent/template.json +4 -0
  100. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  101. package/core/registry/package-templates/capability-provider/template.json +11 -0
  102. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  103. package/core/registry/package-templates/debate-preset/template.json +4 -0
  104. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  105. package/core/registry/package-templates/review-preset/template.json +4 -0
  106. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  107. package/core/registry/package-templates/skill/template.json +4 -0
  108. package/core/registry/package-templates/worker/files/worker.json +92 -0
  109. package/core/registry/package-templates/worker/template.json +11 -0
  110. package/core/registry/packages/doflow/doflow-package.json +15 -0
  111. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  112. package/core/registry/protocols.json +61 -0
  113. package/core/registry/routes.json +17 -0
  114. package/core/registry/rules.json +431 -0
  115. package/core/registry/settings.json +276 -0
  116. package/core/registry/storage.json +701 -30
  117. package/core/registry/telemetry.json +236 -0
  118. package/core/registry/transcripts.json +142 -0
  119. package/core/registry/verification.json +76 -1
  120. package/core/registry/workflows.json +55 -0
  121. package/core/shared/agent-specs/core-implementer.md +3 -0
  122. package/core/shared/agent-specs/delivery-lead.md +68 -0
  123. package/core/shared/agent-specs/quality-guardian.md +5 -1
  124. package/core/shared/agent-specs/research-writer.md +10 -3
  125. package/core/shared/agent-specs/reviewer.md +53 -0
  126. package/core/shared/agent-specs/spec-analyst.md +5 -6
  127. package/core/shared/agent-specs/system-architect.md +5 -1
  128. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  129. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  130. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  131. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  132. package/core/shared/guidance/pointers/codex.md +1 -4
  133. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  134. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  135. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  136. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  137. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  138. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  139. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  140. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  141. package/core/shared/guidance/rules/RULES.md +45 -0
  142. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  143. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  144. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  145. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  146. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  147. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  148. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  149. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  150. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  151. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  152. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  153. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  154. package/core/shared/scripts/doflow/bin/doflow-run +154 -35
  155. package/core/shared/skills/do/SKILL.md +12 -10
  156. package/core/shared/skills/do/references/advice_calls.md +26 -0
  157. package/core/shared/skills/do/references/pm_routing.md +1 -2
  158. package/core/shared/skills/do/references/program_mode.md +31 -0
  159. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  160. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  161. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  162. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  163. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  164. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  165. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  166. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  167. package/core/shared/skills/do-design/SKILL.md +24 -23
  168. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  169. package/core/shared/skills/do-document/SKILL.md +11 -1
  170. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  171. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  172. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  173. package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
  174. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  175. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  176. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
  177. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  178. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  179. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  180. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  181. package/core/shared/skills/do-flow/SKILL.md +8 -9
  182. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  183. package/core/shared/skills/do-git/SKILL.md +4 -0
  184. package/core/shared/skills/do-implement/SKILL.md +7 -9
  185. package/core/shared/skills/do-plan/SKILL.md +29 -19
  186. package/core/shared/skills/do-test/SKILL.md +7 -4
  187. package/core/shared/templates/doflow/constitution-template.md +9 -3
  188. package/core/shared/templates/doflow/data-model-template.md +1 -1
  189. package/core/shared/templates/doflow/design-template.md +113 -16
  190. package/core/shared/templates/doflow/plan-template.md +11 -10
  191. package/core/shared/templates/doflow/question-log-template.md +14 -2
  192. package/core/shared/templates/doflow/requirement-template.md +1 -1
  193. package/core/worker-evidence/antigravity/checks.json +14 -0
  194. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  195. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  196. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  197. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  198. package/core/worker-evidence/codex/checks.json +38 -0
  199. package/package.json +10 -6
  200. package/src/adapters/antigravity/index.js +226 -113
  201. package/src/adapters/claude/index.js +39 -5
  202. package/src/adapters/codex/agents.js +25 -15
  203. package/src/adapters/codex/hooks.js +40 -5
  204. package/src/adapters/codex/index.js +100 -22
  205. package/src/adapters/codex/native-projection.js +6 -2
  206. package/src/adapters/copilot/index.js +58 -23
  207. package/src/adapters/copy-tree.js +294 -48
  208. package/src/adapters/guidance-manifest.js +71 -0
  209. package/src/adapters/guidance-pointer.js +19 -0
  210. package/src/adapters/hook-commands.js +6 -10
  211. package/src/adapters/index.js +1 -0
  212. package/src/adapters/kiro/index.js +38 -13
  213. package/src/adapters/opencode/index.js +8 -8
  214. package/src/adapters/pi/hooks.js +120 -0
  215. package/src/adapters/pi/index.js +48 -16
  216. package/src/adapters/resolve-source.js +26 -2
  217. package/src/cli/commands/install.js +3 -2
  218. package/src/cli/commands/reconcile.js +53 -9
  219. package/src/cli/commands/remove.js +2 -2
  220. package/src/cli/commands/rollback.js +6 -3
  221. package/src/cli/commands/self-update.js +18 -9
  222. package/src/cli/commands/status.js +21 -2
  223. package/src/cli/commands/ui.js +99 -0
  224. package/src/cli/commands/update.js +3 -2
  225. package/src/cli/index.js +338 -33
  226. package/src/cli/runtime-commands.js +243 -52
  227. package/src/cli/shared.js +0 -2
  228. package/src/helper/git.js +1 -1
  229. package/src/helper/guardrails.js +1 -1
  230. package/src/helper/marker-merge.js +31 -13
  231. package/src/helper/package-ids.js +37 -0
  232. package/src/helper/semver.js +94 -0
  233. package/src/install/backup.js +105 -15
  234. package/src/install/context.js +1 -1
  235. package/src/install/targets.js +31 -16
  236. package/src/lifecycle/coverage.js +74 -0
  237. package/src/lifecycle/hook-step.js +61 -0
  238. package/src/lifecycle/hook-tier.js +160 -0
  239. package/src/lifecycle/index.js +434 -86
  240. package/src/lifecycle/retired-harnesses.js +318 -0
  241. package/src/lifecycle/view.js +35 -15
  242. package/src/registry/index.js +686 -32
  243. package/src/registry/worker-ids.js +45 -0
  244. package/src/release/build-package.js +0 -3
  245. package/src/release/docs-blocks.js +492 -0
  246. package/src/release/preflight.js +425 -0
  247. package/src/release/rehearse.js +401 -0
  248. package/src/release/release-workflow.js +733 -0
  249. package/src/release/sync-plugin-versions.js +43 -7
  250. package/src/runtime/advice/bench-gate.js +25 -0
  251. package/src/runtime/advice/cli.js +59 -16
  252. package/src/runtime/advice/client.js +32 -9
  253. package/src/runtime/advice/config.js +98 -133
  254. package/src/runtime/advice/doctor.js +1 -1
  255. package/src/runtime/advice/elevation.js +97 -0
  256. package/src/runtime/advice/endpoint-guard.js +9 -3
  257. package/src/runtime/advice/mode.js +37 -0
  258. package/src/runtime/advice/record.js +18 -5
  259. package/src/runtime/advice/registry.js +4 -1
  260. package/src/runtime/advice/shadow-config.js +229 -0
  261. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  262. package/src/runtime/brief-rules.js +81 -0
  263. package/src/runtime/brief-tools.js +111 -34
  264. package/src/runtime/candidate.js +514 -0
  265. package/src/runtime/capabilities/executor.js +450 -0
  266. package/src/runtime/capabilities/loopback.js +41 -0
  267. package/src/runtime/capabilities/sanitizer.js +88 -0
  268. package/src/runtime/capability-router.js +135 -3
  269. package/src/runtime/checkouts.js +73 -11
  270. package/src/runtime/claims.js +27 -12
  271. package/src/runtime/cli-result.js +11 -4
  272. package/src/runtime/cli.js +221 -277
  273. package/src/runtime/config-home.js +42 -0
  274. package/src/runtime/context/arbitrate.js +767 -0
  275. package/src/runtime/context/budget.js +203 -0
  276. package/src/runtime/context/compose.js +1367 -0
  277. package/src/runtime/context/delivery-store.js +100 -0
  278. package/src/runtime/context/emission-rules.js +118 -0
  279. package/src/runtime/context/identity.js +227 -0
  280. package/src/runtime/context/mandatory.js +594 -0
  281. package/src/runtime/context/pointer.js +142 -0
  282. package/src/runtime/context/primary-command.js +117 -0
  283. package/src/runtime/context/primary.js +344 -0
  284. package/src/runtime/context/summary-store.js +655 -0
  285. package/src/runtime/context-pack.js +1106 -245
  286. package/src/runtime/decision-register.js +13 -4
  287. package/src/runtime/dispatch/admission.js +392 -0
  288. package/src/runtime/dispatch/attempt-end.js +274 -0
  289. package/src/runtime/dispatch/command.js +628 -0
  290. package/src/runtime/dispatch/controls.js +904 -0
  291. package/src/runtime/dispatch/envelope.js +276 -0
  292. package/src/runtime/dispatch/grants.js +310 -0
  293. package/src/runtime/dispatch/integrate.js +617 -0
  294. package/src/runtime/dispatch/loop.js +1835 -0
  295. package/src/runtime/dispatch/store.js +352 -0
  296. package/src/runtime/dispatch/verify-run.js +201 -0
  297. package/src/runtime/evidence-fields.js +39 -0
  298. package/src/runtime/evidence-ledger.js +41 -7
  299. package/src/runtime/failure/capture.js +3 -1
  300. package/src/runtime/failure/cli.js +25 -9
  301. package/src/runtime/failure/home.js +57 -30
  302. package/src/runtime/failure/store.js +7 -2
  303. package/src/runtime/feature-pointer.js +174 -0
  304. package/src/runtime/feature-resolve.js +13 -2
  305. package/src/runtime/fleet/command.js +77 -0
  306. package/src/runtime/fleet/projects.js +178 -0
  307. package/src/runtime/fleet/tree.js +383 -0
  308. package/src/runtime/freshness.js +77 -10
  309. package/src/runtime/git-memo.js +46 -0
  310. package/src/runtime/guidance/ablation.js +427 -0
  311. package/src/runtime/health.js +565 -17
  312. package/src/runtime/history-compactor.js +13 -3
  313. package/src/runtime/implementation-gate.js +277 -22
  314. package/src/runtime/inventory/coverage.js +1 -1
  315. package/src/runtime/inventory/index.js +1 -1
  316. package/src/runtime/inventory/precedence.js +2 -2
  317. package/src/runtime/inventory/siblings.js +6 -4
  318. package/src/runtime/knowledge/code-corpus.js +277 -0
  319. package/src/runtime/knowledge/code-lexical.js +130 -0
  320. package/src/runtime/knowledge/code-retrieval.js +307 -0
  321. package/src/runtime/knowledge/code-validation.js +106 -0
  322. package/src/runtime/knowledge/gp3-gate.js +74 -0
  323. package/src/runtime/knowledge/graph-adapter.js +288 -0
  324. package/src/runtime/knowledge/graph-fallback.js +394 -0
  325. package/src/runtime/knowledge/hybrid.js +136 -8
  326. package/src/runtime/knowledge/retrieval.js +34 -1
  327. package/src/runtime/knowledge/rtk-compress.js +77 -0
  328. package/src/runtime/knowledge/semble-bridge.py +106 -0
  329. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  330. package/src/runtime/knowledge/semble-provider.js +365 -0
  331. package/src/runtime/lifecycle/cli.js +16 -6
  332. package/src/runtime/lifecycle/event-store.js +8 -6
  333. package/src/runtime/lifecycle/followup.js +6 -1
  334. package/src/runtime/lifecycle/overview.js +24 -1
  335. package/src/runtime/lifecycle/report-store.js +8 -10
  336. package/src/runtime/lifecycle/retention.js +95 -31
  337. package/src/runtime/lifecycle/root.js +9 -1
  338. package/src/runtime/lifecycle/status.js +34 -17
  339. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  340. package/src/runtime/mask.js +134 -7
  341. package/src/runtime/mcp-consultations.js +40 -6
  342. package/src/runtime/model-router.js +79 -7
  343. package/src/runtime/models/catalogue.js +191 -0
  344. package/src/runtime/models/command.js +153 -0
  345. package/src/runtime/off-words.js +12 -0
  346. package/src/runtime/outcome.js +3 -3
  347. package/src/runtime/packages/command.js +1009 -0
  348. package/src/runtime/packages/contributions.js +116 -0
  349. package/src/runtime/packages/digest.js +132 -0
  350. package/src/runtime/packages/fetch.js +182 -0
  351. package/src/runtime/packages/grants.js +192 -0
  352. package/src/runtime/packages/index-file.js +134 -0
  353. package/src/runtime/packages/instruction.js +90 -0
  354. package/src/runtime/packages/manifest.js +331 -0
  355. package/src/runtime/packages/points.js +477 -0
  356. package/src/runtime/packages/programs.js +216 -0
  357. package/src/runtime/packages/scaffold.js +277 -0
  358. package/src/runtime/packages/source.js +90 -0
  359. package/src/runtime/packages/state.js +164 -0
  360. package/src/runtime/packages/store.js +449 -0
  361. package/src/runtime/packages/tar.js +106 -0
  362. package/src/runtime/packages/view.js +406 -0
  363. package/src/runtime/plan-check.js +59 -0
  364. package/src/runtime/plan-content.js +324 -0
  365. package/src/runtime/policy-init.js +118 -0
  366. package/src/runtime/profiles/doctor.js +96 -0
  367. package/src/runtime/profiles/resolve.js +244 -0
  368. package/src/runtime/profiles/rollback.js +254 -0
  369. package/src/runtime/profiles/sampler.js +41 -0
  370. package/src/runtime/readiness-record.js +16 -11
  371. package/src/runtime/readiness.js +127 -16
  372. package/src/runtime/research-request.js +50 -25
  373. package/src/runtime/retrieval-plan.js +402 -73
  374. package/src/runtime/retrieval-record.js +293 -0
  375. package/src/runtime/review/command.js +246 -0
  376. package/src/runtime/review/dominance.js +68 -0
  377. package/src/runtime/review/findings.js +434 -0
  378. package/src/runtime/review/identity.js +255 -0
  379. package/src/runtime/review/protocol-command.js +109 -0
  380. package/src/runtime/review/protocols.js +405 -0
  381. package/src/runtime/review/resolve-protocol.js +302 -0
  382. package/src/runtime/review/start.js +530 -0
  383. package/src/runtime/roadmap/facts.js +479 -0
  384. package/src/runtime/roadmap/gates.js +270 -0
  385. package/src/runtime/roadmap/loop.js +1636 -0
  386. package/src/runtime/roadmap/merge.js +381 -0
  387. package/src/runtime/roadmap/rows.js +1113 -0
  388. package/src/runtime/roadmap/seed.js +951 -0
  389. package/src/runtime/rules-report.js +336 -0
  390. package/src/runtime/scaffold/artifacts.js +63 -9
  391. package/src/runtime/scaffold/generate.js +21 -9
  392. package/src/runtime/settings/autopilot.js +106 -0
  393. package/src/runtime/settings/cli.js +280 -0
  394. package/src/runtime/settings/config-write.js +314 -0
  395. package/src/runtime/settings/files.js +271 -0
  396. package/src/runtime/settings/legacy.js +154 -0
  397. package/src/runtime/settings/lexical.js +56 -0
  398. package/src/runtime/settings/prepared-run.js +269 -0
  399. package/src/runtime/settings/project-layers.js +118 -0
  400. package/src/runtime/settings/refs.js +288 -0
  401. package/src/runtime/settings/registry.js +553 -0
  402. package/src/runtime/settings/resolve.js +713 -0
  403. package/src/runtime/settings/shared.js +37 -0
  404. package/src/runtime/settings/worker-billing.js +107 -0
  405. package/src/runtime/settings/write.js +269 -0
  406. package/src/runtime/slot-command.js +78 -0
  407. package/src/runtime/slots.js +671 -0
  408. package/src/runtime/source-version.js +290 -0
  409. package/src/runtime/state-root.js +217 -0
  410. package/src/runtime/storage/cli.js +12 -5
  411. package/src/runtime/storage/engine.js +9 -2
  412. package/src/runtime/storage/registry.js +14 -1
  413. package/src/runtime/storage/roots.js +66 -16
  414. package/src/runtime/storage/rules.js +82 -10
  415. package/src/runtime/storage/upkeep.js +10 -2
  416. package/src/runtime/storage/work-status.js +245 -26
  417. package/src/runtime/task-attempts.js +589 -69
  418. package/src/runtime/task-classifier.js +47 -11
  419. package/src/runtime/task-graph.js +373 -0
  420. package/src/runtime/task-run.js +778 -0
  421. package/src/runtime/task-scope.js +202 -56
  422. package/src/runtime/task-sources.js +147 -0
  423. package/src/runtime/task-state.js +440 -65
  424. package/src/runtime/team/admission.js +301 -0
  425. package/src/runtime/team/envelope.js +324 -0
  426. package/src/runtime/team/review-guard.js +156 -0
  427. package/src/runtime/trace/analyze.js +427 -0
  428. package/src/runtime/trace/baseline.js +770 -0
  429. package/src/runtime/trace/ledger.js +63 -140
  430. package/src/runtime/trace/otlp-export.js +568 -0
  431. package/src/runtime/trace/otlp-map.js +481 -0
  432. package/src/runtime/trace/spans.js +745 -0
  433. package/src/runtime/trace/telemetry.js +225 -0
  434. package/src/runtime/trace/views.js +1 -0
  435. package/src/runtime/transcripts/command.js +98 -0
  436. package/src/runtime/transcripts/cursor.js +326 -0
  437. package/src/runtime/transcripts/read.js +126 -0
  438. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  439. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  440. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  441. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  442. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  443. package/src/runtime/transcripts/registry.js +161 -0
  444. package/src/runtime/transcripts/stats.js +161 -0
  445. package/src/runtime/verification/check-key.js +628 -0
  446. package/src/runtime/verification/contract-runner.js +546 -11
  447. package/src/runtime/verification/engine.js +499 -9
  448. package/src/runtime/verification/impact.js +617 -0
  449. package/src/runtime/verification/inflight.js +205 -0
  450. package/src/runtime/verification/registry.js +176 -0
  451. package/src/runtime/verification/reuse.js +184 -0
  452. package/src/runtime/verification/telemetry-emit.js +254 -0
  453. package/src/runtime/verification/test-selection.js +67 -7
  454. package/src/runtime/worker/active.js +90 -0
  455. package/src/runtime/worker/cancel.js +222 -0
  456. package/src/runtime/worker/catalogue.js +122 -0
  457. package/src/runtime/worker/command.js +677 -0
  458. package/src/runtime/worker/evidence.js +288 -0
  459. package/src/runtime/worker/launch.js +288 -0
  460. package/src/runtime/worker/models.js +137 -0
  461. package/src/runtime/worker/parsers.js +218 -0
  462. package/src/runtime/worker/probes/index.js +706 -0
  463. package/src/runtime/worker/process-tree.js +415 -0
  464. package/src/runtime/worker/quota.js +43 -0
  465. package/src/runtime/worker/readiness.js +713 -0
  466. package/src/runtime/worker/run-store.js +166 -0
  467. package/src/runtime/worker/supervisor.js +541 -0
  468. package/src/runtime/worker/worktree-check.js +252 -0
  469. package/src/runtime/workflow-engine.js +5 -1
  470. package/src/runtime/workflow-orchestrator.js +578 -63
  471. package/src/runtime/worktree.js +149 -22
  472. package/src/state/index.js +1 -1
  473. package/src/state/lockfile.js +64 -10
  474. package/src/state/run-lock.js +1 -1
  475. package/types/node-commonjs.d.ts +106 -7
  476. package/core/.antigravity-plugin/hooks.json +0 -39
  477. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  478. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  479. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
  480. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  481. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
  482. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  483. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
  484. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  485. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  486. package/core/harnesses/gemini/hooks/session-start.sh +0 -5
  487. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  488. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  489. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  490. package/core/harnesses/shared/hooks/hooks.json +0 -47
  491. package/core/shared/guidance/FLAGS.md +0 -25
  492. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  493. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  494. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  495. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  496. package/src/adapters/gemini/hooks.js +0 -210
  497. package/src/adapters/gemini/index.js +0 -390
@@ -18,12 +18,14 @@
18
18
  // handle<Name>Command(...)` — to cross-check the dispatcher's verb table in both directions. Keep
19
19
  // every case in exactly that shape.
20
20
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
21
- const fs = require('node:fs');
22
21
  const os = require('node:os');
23
22
  const path = require('node:path');
24
23
  const {
25
24
  handleCapabilitiesCommand, handleReadinessCommand, handleEvidenceCommand,
25
+ handleContextEmitCommand, handleContextGrantCommand,
26
26
  } = require('../runtime/cli.js');
27
+ const { loadRegistry } = require('../registry/index.js');
28
+ const { enforcementFor, UnknownEnforcementTarget } = require('../lifecycle/coverage.js');
27
29
  // `doctor` comes from the health module rather than src/runtime/cli.js: the health-probe report
28
30
  // (FR-013) supersedes the presence-check version, and one verb must have one implementation.
29
31
  const { handleDoctorCommand } = require('../runtime/health.js');
@@ -39,37 +41,131 @@ const { handleWorkflowCommand } = require('../runtime/workflow-engine.js');
39
41
  const { handleOrchestrateCommand } = require('../runtime/workflow-orchestrator.js');
40
42
  const { handleResearchRequestCommand } = require('../runtime/research-request.js');
41
43
  const { handleTaskAttemptCommand } = require('../runtime/task-attempts.js');
44
+ const { handleWorkerCommand } = require('../runtime/worker/command.js');
45
+ const { handleDispatchCommand } = require('../runtime/dispatch/command.js');
42
46
  const { handleRetrieveCommand } = require('../runtime/knowledge/retrieval.js');
43
47
  const { handleModelRoleCommand } = require('../runtime/model-router.js');
44
48
  const { handleRouteCommand } = require('../runtime/capability-router.js');
45
49
  const { handleContextPackCommand } = require('../runtime/context-pack.js');
46
50
  const { handleRetrievalPlanCommand } = require('../runtime/retrieval-plan.js');
47
51
  const { handleOutcomeCommand } = require('../runtime/outcome.js');
48
- const { handleVerifyCommand } = require('../runtime/verification/engine.js');
52
+ const { handleVerifyCommand, verifyStateRoot } = require('../runtime/verification/engine.js');
49
53
  const { handleLeakScanCommand } = require('../runtime/leak-scan.js');
50
54
  const { handleRecoverCommand } = require('../runtime/recovery.js');
51
55
  const { handleScaffoldCommand } = require('../runtime/scaffold/generate.js');
52
- const { handleDecisionCommand } = require('../runtime/decision-register.js');
56
+ const { handleDecisionCommand, isReservedChannel, CHANNEL_RESERVED } = require('../runtime/decision-register.js');
53
57
  const { handleFollowupCommand, handleLifecycleCommand, handleGoalCommand } = require('../runtime/lifecycle/cli.js');
54
58
  const { projectRoot: lifecycleProjectRoot } = require('../runtime/lifecycle/root.js');
55
59
  const { upkeepAfterVerb } = require('../runtime/storage/upkeep.js');
56
60
  const { handleStorageCommand } = require('../runtime/storage/cli.js');
61
+ const { handleRoadmapCommand, resolveMainRoot } = require('../runtime/roadmap/loop.js');
62
+ const { handlePlanCheckCommand } = require('../runtime/plan-check.js');
63
+ const { handleSlotCommand } = require('../runtime/slot-command.js');
64
+ const { handleReviewCommand } = require('../runtime/review/command.js');
65
+ const { handleProtocolCommand } = require('../runtime/review/protocol-command.js');
66
+ const { handleRulesCommand } = require('../runtime/rules-report.js');
67
+ const { handlePackageCommand } = require('../runtime/packages/command.js');
68
+ const { handleModelsCommand } = require('../runtime/models/command.js');
69
+ // `plan-check` is read-only (115 IC-001): no upkeep, which would write `.gitignore` and `upkeep-due`.
70
+ // `review` writes only its one telemetry line (095 IC-003).
71
+ // `protocol` only prints the registry and the resolved protocol (119 IC-004), so it has nothing to keep up.
72
+ // `rules` only prints the values the runtime owns, with their sources, and writes nothing.
73
+ // `primary` only prints the resolved main session (120 IC-008) and writes nothing.
74
+ // `fleet` only lists the projects this machine knows from two read-only stores (093 IC-019) and writes nothing.
75
+ // `transcript` only reads agents' transcripts and DoFlow's own records and prints them (093 IC-022, IC-023); it writes nothing.
76
+ // `package` stays here (122 IC-012): `new` writes only the target folder; `add --plan`, `update --plan` and `remove` write the store and lock and run upkeep (see `runsUpkeep`).
77
+ // `slot` is not here: `run`, `list` and `release` write the slot files and run upkeep; only `slot --action peek` is read-only and skips it (093 IC-018 b, see `runsUpkeep`).
78
+ const NO_UPKEEP = new Set(['storage', 'config', 'plan-check', 'review', 'protocol', 'rules', 'primary', 'package', 'models', 'fleet', 'transcript']);
79
+ const runsUpkeep = (o ) => (!NO_UPKEEP.has(o.cmd) && !(o.cmd === 'slot' && o.actionGiven && o.action === 'peek'))
80
+ || (o.cmd === 'package' && ((typeof o.planId === 'string' && (o.action === 'add' || o.action === 'update')) || o.action === 'remove'));
57
81
  // Required on first use, so a CLI whose failure modules cannot be loaded still runs every other verb.
58
82
  function handleFailureCommand(options ) { return require('../runtime/failure/cli.js').handleFailureCommand(options); }
83
+ // The seed validator loads only for `roadmap seed`, and only this file may require it (G54).
84
+ function handleSeedCommand(options ) { return require('../runtime/roadmap/seed.js').handleSeedCommand(options); }
85
+ // maint-thin: `roadmap promote <id...>` lives in seed.ts too, so it stays out of every autopilot module (G54).
86
+ function handlePromoteCommand(options ) { return require('../runtime/roadmap/seed.js').handlePromoteCommand(options); }
87
+ // Settings support is needed only when the config verb runs.
88
+ function handleConfigCommand(options ) { return require('../runtime/settings/cli.js').handleConfigCommand(options); }
89
+ // The primary verb (row 120 A.4) needs the settings resolver only when it runs.
90
+ function handlePrimaryCommand(options ) { return require('../runtime/context/primary-command.js').handlePrimaryCommand(options); }
91
+ // The fleet verb (093 V.3) loads only when it runs.
92
+ function handleFleetCommand(options ) { return require('../runtime/fleet/command.js').handleFleetCommand(options); }
93
+ // The transcript verb (093 V.6) loads only when it runs.
94
+ function handleTranscriptCommand(options ) { return require('../runtime/transcripts/command.js').handleTranscriptCommand(options); }
59
95
  const { handleInventoryCommand } = require('../runtime/inventory/index.js');
60
96
  const { finishRuntime, usageError } = require('../runtime/cli-result.js');
61
- const { setDefaultSlug, invalidSlugRefusal, slugNamesNoFeature } = require('../runtime/task-scope.js');
62
- const { listCheckouts } = require('../runtime/checkouts.js');
97
+ const { setRecordSlug, callerFeatureSlug, invalidSlugRefusal, slugNamesNoFeature } = require('../runtime/task-scope.js');
98
+ const { listCheckouts, mayBeLinked } = require('../runtime/checkouts.js');
99
+ const { taskStateRoot, sweepWorktreeRecords } = require('../runtime/state-root.js');
63
100
  const { featureSlugFor } = require('../runtime/implementation-gate.js');
64
101
  const { handleBriefToolsCommand } = require('../runtime/brief-tools.js');
65
- const { handleAdviseCommand } = require('../runtime/advice/cli.js');
102
+ // Advice writers load settings/write.ts, so keep that surface off installer and other verb paths.
103
+ function handleAdviseCommand(options ) { return require('../runtime/advice/cli.js').handleAdviseCommand(options); }
66
104
  const { REPO_ROOT, buildAdapterRegistry } = require('./shared.js');
67
105
 
68
106
  /** Where `readiness`/`evidence` read and write per-task state. Mirrors scopeOf()'s rules so these
69
107
  * commands are scope-aware like the rest of the CLI, rather than defaulting to the DoFlow install
70
108
  * directory — which for an npm install is inside node_modules/. */
71
109
  function evidenceRoot(o ) {
72
- return o.global ? os.homedir() : path.resolve(o.positional[0] || '.');
110
+ if (o.global) return os.homedir();
111
+ // `roadmap seed [<project>]` (113 IC-001): the sub-verb takes the first positional slot, so the project root is the one after it.
112
+ if (o.cmd === 'roadmap' && o.positional[0] === 'seed') return path.resolve(o.positional[1] || '.');
113
+ return path.resolve(o.positional[0] || '.');
114
+ }
115
+
116
+ /** The `trace` views that take the first positional slot: `spans` (088 IC-011), `analyze` (091 IC-001), `export` (091 IC-006). */
117
+ const TRACE_VIEWS = new Set(['spans', 'analyze', 'export']);
118
+
119
+ /** `trace spans|analyze|export [<project>]`: the view name takes the first positional slot, so the
120
+ * project root is the one after it. Every other call is `evidenceRoot(o)`. */
121
+ function traceViewRoot(o ) {
122
+ return o.cmd === 'trace' && TRACE_VIEWS.has(o.positional[0]) && !o.global ? path.resolve(o.positional[1] || '.') : evidenceRoot(o);
123
+ }
124
+
125
+ /** `roadmap`: the runner, the `seed` sub-verb (113 IC-001), or the `promote` sub-verb (maint-thin), each taking the
126
+ * first positional slot and setting the exit code itself; `promote` runs on the main checkout of the working directory. */
127
+ function handleRoadmapVerbCommand(o ) {
128
+ const action = o.actionGiven ? o.action : undefined;
129
+ if (o.positional[0] === 'seed') return finishRuntime(handleSeedCommand({ action, draft: o.draft, json: o.json, cwd: process.cwd(), root: resolveMainRoot(evidenceRoot(o)) }));
130
+ if (o.positional[0] === 'promote') return finishRuntime(handlePromoteCommand({ ids: o.positional.slice(1), json: o.json, root: resolveMainRoot(process.cwd()) }));
131
+ // `init-policy --commit` commits the policy onto develop; it lives outside the loop's tree (G53, G54). Any other action with --commit is the loop's exit 2.
132
+ if (o.commit && action === 'init-policy') return require('../runtime/policy-init.js').commitPolicyCommand({ json: o.json, root: resolveMainRoot(evidenceRoot(o)), repoRoot: REPO_ROOT });
133
+ return handleRoadmapCommand({ action, check: o.check, commit: o.commit, json: o.json, run: o.run, row: o.row, cwd: evidenceRoot(o), repoRoot: REPO_ROOT });
134
+ }
135
+
136
+ /**
137
+ * Where `trace analyze --emit followups --task-id` reads the task's evidence ledger: the store root
138
+ * and record slug the `evidence` verb writes under (115 IC-004, C-1), so a linked worktree reads the
139
+ * main checkout's namespaced ledger. Read only: no sweep, so a record still left in the worktree's
140
+ * own store is not cited. Undefined when no ledger is read; the handler refuses the flag mix itself.
141
+ */
142
+ function analyzeLedgerRoot(o ) {
143
+ if (o.emit !== 'followups' || typeof o.taskId !== 'string') return undefined;
144
+ const caller = traceViewRoot(o);
145
+ const checkouts = !o.global && mayBeLinked(caller) ? listCheckouts({ cwd: caller }) : null;
146
+ setRecordSlug(() => o.slug || callerFeatureSlug({ projectRoot: caller, taskId: o.taskId }) || null);
147
+ return taskStateRoot({ global: o.global, positional: [caller] }, checkouts).root;
148
+ }
149
+
150
+ /** `trace`: the ledger trajectory, unchanged, or the `spans`, `analyze` or `export` view, whose modules load
151
+ * only for that view so `trace` and `stats` read nothing new (NFR-010, 091 NFR-009). */
152
+ function handleTraceViewCommand(o ) {
153
+ if (o.positional[0] === 'analyze') {
154
+ return require('../runtime/trace/analyze.js').handleTraceAnalyze({ positional: o.positional.slice(1), run: o.run, json: o.json, global: o.global,
155
+ emit: o.emit, taskId: o.taskId, stateRoot: analyzeLedgerRoot(o) });
156
+ }
157
+ if (o.positional[0] === 'export') {
158
+ return require('../runtime/trace/otlp-export.js').handleTraceExport({
159
+ positional: o.positional.slice(1),
160
+ run: o.run,
161
+ json: o.json,
162
+ global: o.global,
163
+ dryRun: o.dryRun,
164
+ otlpHttp: o.otlpHttp,
165
+ });
166
+ }
167
+ if (o.positional[0] !== 'spans') return handleTraceCommand({ json: o.json, days: o.days, global: o.global, projectRoot: evidenceRoot(o) });
168
+ return require('../runtime/trace/spans.js').handleTraceSpans({ positional: o.positional.slice(1), run: o.run, json: o.json, global: o.global });
73
169
  }
74
170
 
75
171
  /**
@@ -111,6 +207,9 @@ function evidenceItemFromFlags(o ) {
111
207
  if (o.locator !== undefined) item.locator = o.locator;
112
208
  if (o.content !== undefined) item.content = o.content;
113
209
  if (o.establishes !== undefined) item.establishes = o.establishes;
210
+ if (o.need !== undefined) item.need = o.need;
211
+ if (o.result !== undefined) item.result = o.result;
212
+ if (o.absence !== undefined) item.absence = o.absence;
114
213
  // Both halves of an observation travel together; a lone half must reach the write boundary as
115
214
  // the partial object it is, so the refusal names the missing field instead of dropping the pair.
116
215
  if (o.observedCommand !== undefined || o.observedExit !== undefined) {
@@ -125,28 +224,21 @@ function evidenceItemFromFlags(o ) {
125
224
  return item;
126
225
  }
127
226
 
128
- /** Verbs that read or write per-task records, and so accept `--slug` (IC-002). */
129
- const TASK_STORE_VERBS = new Set(['evidence', 'claim', 'readiness', 'context-pack', 'research-request', 'outcome', 'retrieval-plan']);
227
+ /** Verbs that read or write per-task records, and so accept `--slug` (IC-002). Their records, and
228
+ * `orchestrate`'s run and `context-emit`'s delivery, live in `taskStateRoot` (115 IC-004). */
229
+ const TASK_STORE_VERBS = new Set(['evidence', 'claim', 'readiness', 'context-pack', 'research-request', 'outcome', 'retrieval-plan', 'retrieve']);
130
230
 
131
- /** Verbs that build a task's readiness: run from a linked worktree whose feature folder lives only
132
- * in the main checkout, they keep the task's records there, beside the folder their evidence names. */
133
- const FEATURE_STORE_VERBS = new Set(['evidence', 'claim', 'readiness']);
231
+ /** The checkout a call runs in: `context-emit`'s `--cwd`, else the positional project root. */
232
+ function callerRoot(o ) {
233
+ return o.cmd === 'context-emit' && typeof o.cwd === 'string' && o.cwd !== '' ? path.resolve(o.cwd) : evidenceRoot(o);
234
+ }
134
235
 
135
- /**
136
- * The main checkout, when `o` runs one of FEATURE_STORE_VERBS in a linked worktree that lacks the
137
- * task's feature folder and the main checkout holds it; null otherwise. The feature is `--slug`,
138
- * else the branch's. A locator such as `agent-docs/doflow/<slug>/plan.md` resolves only there, and
139
- * the readiness record written there is the one verify, orchestrate and the edit hook read from
140
- * the worktree.
141
- */
142
- function featureStoreRoot(o ) {
143
- if (!FEATURE_STORE_VERBS.has(o.cmd) || o.global) return null;
144
- const checkouts = listCheckouts({ cwd: evidenceRoot(o) });
145
- if (!checkouts.isLinked) return null;
146
- const slug = featureSlugFor({ projectRoot: checkouts.current, slug: o.slug });
147
- const folder = (root ) => path.join(root, 'agent-docs', 'doflow', slug || '');
148
- if (!slug || fs.existsSync(folder(checkouts.current)) || !fs.existsSync(folder(checkouts.main))) return null;
149
- return { root: checkouts.main, slug };
236
+ /** The store conflicts a sweep found, one line each (IC-006). */
237
+ function conflictText(o , sweep ) {
238
+ const lines = sweep.conflicts.map((c ) => (c.reason === 'link' ? ` ${c.worktree} -> ${c.main}: a link on the way, nothing is moved through it`
239
+ : c.reason === 'same-file' ? ` ${c.worktree} and ${c.main} are one file` : ` ${c.worktree} differs from ${c.main}`));
240
+ return `doflow ${o.cmd}: store-conflict: ${sweep.conflicts.length} record(s) in this worktree could not be moved to the main checkout; `
241
+ + `files already moved stay moved. Keep one copy of each pair and remove the other, then re-run.\n${lines.join('\n')}`;
150
242
  }
151
243
 
152
244
  /**
@@ -156,6 +248,14 @@ function featureStoreRoot(o ) {
156
248
  * to the engine module each case names.
157
249
  */
158
250
  function dispatchRuntimeCommandInner(o , moment ) {
251
+ // IC-005: no caller names a decider. orchestrate takes no --by, and the channel `policy` (decided
252
+ // by autopilot) is refused on every verb, before any state is read or written.
253
+ if (o.cmd === 'orchestrate' && o.by !== undefined) {
254
+ return usageError('orchestrate', '--by is not accepted; the runtime derives the gate decider', o.json);
255
+ }
256
+ if (isReservedChannel(o.channel)) {
257
+ return usageError(o.cmd, CHANNEL_RESERVED, o.json);
258
+ }
159
259
  // A slug becomes a directory name and a state key, so one that could name a path is refused
160
260
  // here, once, for every verb that reads `--slug`, before any of them reads or writes state
161
261
  // (FR-013, IC-009). A well-formed slug that names no feature is the verbs' own business.
@@ -169,34 +269,75 @@ function dispatchRuntimeCommandInner(o , moment ) {
169
269
  // here so the verbs whose handlers build their own ledger (readiness, evidence) route the same
170
270
  // way as the ones that take `slug` directly. The orchestration journal is keyed by slug already.
171
271
  // Reset on every dispatch, so one invocation's slug can never carry into the next in-process call.
172
- const featureStore = featureStoreRoot(o);
173
- const storeRoot = featureStore ? featureStore.root : evidenceRoot(o);
272
+ // `retrieve` is a task-store verb only as bound code retrieval (--scope code with --task-id or --slug):
273
+ // guidance retrieval and unbound code retrieval record nothing in a feature store, so they keep the
274
+ // pre-079 dispatch (no checkout listing, no feature resolver, no store note).
275
+ const storeVerb = (verbs ) => verbs.has(o.cmd) && (o.cmd !== 'retrieve' || (o.scope === 'code' && Boolean(o.taskId || o.slug)));
276
+ const recordVerb = storeVerb(TASK_STORE_VERBS) || o.cmd === 'orchestrate' || o.cmd === 'context-emit';
277
+ const caller = callerRoot(o);
278
+ // `evidence` always lists: its attempt lookup reuses the main checkout from this one listing.
279
+ const checkouts = recordVerb && !o.global && (o.cmd === 'evidence' || mayBeLinked(caller))
280
+ ? listCheckouts({ cwd: caller }) : null;
281
+ // 115 IC-004: a linked, non-sandbox worktree keeps every per-task record in the main checkout.
282
+ const { root: storeRoot, redirected } = recordVerb ? taskStateRoot(o, checkouts) : { root: evidenceRoot(o), redirected: false };
283
+ const attemptRoot = checkouts?.ok && !checkouts.sandbox ? (checkouts.main || checkouts.current || null) : null;
174
284
  // Use the same root resolver as the handler: task redirection, the nearest run ledger,
175
285
  // and lifecycle's shared main-worktree store are deliberately different scopes.
176
286
  try {
177
287
  if (['trace', 'stats', 'discover'].includes(o.cmd)) {
178
- moment.projectRoot = path.dirname(resolveRunsLocation({ start: evidenceRoot(o), global: o.global }).configDir);
179
- } else if (['followup', 'lifecycle', 'goal', 'task-attempt'].includes(o.cmd)) {
288
+ moment.projectRoot = path.dirname(resolveRunsLocation({ start: traceViewRoot(o), global: o.global }).configDir);
289
+ } else if (['followup', 'lifecycle', 'goal', 'task-attempt', 'worker', 'dispatch'].includes(o.cmd)) {
180
290
  moment.projectRoot = lifecycleProjectRoot(evidenceRoot(o));
291
+ } else if (o.cmd === 'plan-check' || o.cmd === 'slot') {
292
+ moment.projectRoot = process.cwd(); // the first positional of plan-check is a file, never a project root
293
+ } else if (o.cmd === 'verify') {
294
+ moment.projectRoot = verifyStateRoot({ start: evidenceRoot(o) }).projectRoot; // the store verify writes, never a cwd fragment
181
295
  } else {
182
296
  moment.projectRoot = storeRoot;
183
297
  }
184
298
  } catch { /* a failed upkeep lookup must not change the verb's answer */ }
185
- const storeSlug = featureStore ? featureStore.slug : o.slug;
186
- setDefaultSlug(TASK_STORE_VERBS.has(o.cmd) ? storeSlug : null);
187
- if (featureStore) {
188
- console.error(`doflow ${o.cmd}: note: feature '${storeSlug}' lives in the main checkout ${storeRoot}; this task's records are read and written there`);
299
+ // 115 C-1: one record slug per call, `--slug`, else the feature of the checkout the verb runs in,
300
+ // used by every store the call touches (task-scope.ts). It is looked up on first use, so a
301
+ // `context-emit` event that compiles no pack spawns no resolver. Reset on every dispatch, so one
302
+ // invocation's slug can never carry into the next in-process call. `retrieve` is a task-store verb
303
+ // only as bound code retrieval (--scope code with --task-id or --slug).
304
+ let slugMemo ;
305
+ const recordSlug = () => {
306
+ if (slugMemo === undefined) {
307
+ try { slugMemo = o.slug || callerFeatureSlug({ projectRoot: caller, taskId: o.taskId }) || null; } catch { slugMemo = null; }
308
+ }
309
+ return slugMemo ;
310
+ };
311
+ setRecordSlug(recordVerb ? recordSlug : undefined);
312
+ const storeSlug = recordVerb && o.cmd !== 'context-emit' ? recordSlug() : undefined;
313
+ if (redirected && o.cmd !== 'context-emit') {
314
+ console.error(`doflow ${o.cmd}: note: records are kept in the main checkout ${storeRoot}`);
189
315
  }
190
- // A well-formed slug that names no feature changes nothing (the records go to the shared task
191
- // store), but the caller typed it expecting an effect, so say so once, on stderr only.
192
- if (!featureStore && TASK_STORE_VERBS.has(o.cmd) && typeof o.slug === 'string' && o.slug !== ''
193
- && slugNamesNoFeature({ projectRoot: evidenceRoot(o), slug: o.slug })) {
194
- console.error(`doflow ${o.cmd}: note: --slug '${o.slug}' names no feature; using the shared task store`);
316
+ // A well-formed slug that names no feature folder still names the records' namespace, but the
317
+ // caller typed it expecting a feature, so say so once, on stderr only.
318
+ if (storeVerb(TASK_STORE_VERBS) && typeof o.slug === 'string' && o.slug !== ''
319
+ && slugNamesNoFeature({ projectRoots: redirected ? [caller, storeRoot] : [caller], slug: o.slug })) {
320
+ console.error(`doflow ${o.cmd}: note: --slug '${o.slug}' names no feature folder; its records are kept under that slug`);
321
+ }
322
+ // 115 IC-006: every record left in the worktree's own store moves to the main checkout first, in one
323
+ // locked sweep; a record held in both with different bytes refuses the call before the verb runs.
324
+ // 092 IC-001, 093 IC-012: the orchestrate reads (list, status, timeline) write nothing, so they never sweep: they read the
325
+ // main checkout's store and see a record still left in this worktree as another checkout's
326
+ // (`orchestrationRuns`, `findRunFile`). The next write verb from here sweeps it as before.
327
+ const orchestrateRead = o.cmd === 'orchestrate' && (o.action === 'list' || o.action === 'status' || o.action === 'timeline');
328
+ if (redirected && !orchestrateRead) {
329
+ const sweep = sweepWorktreeRecords({ mainRoot: storeRoot, worktreeRoot: checkouts.current, recordSlug });
330
+ if (!sweep.ok) {
331
+ if (o.json) console.log(JSON.stringify(sweep, null, 2));
332
+ else console.error(conflictText(o, sweep));
333
+ return finishRuntime(2);
334
+ }
195
335
  }
196
336
  switch (o.cmd) {
197
337
  // REPO_ROOT locates the capability registry; projectRoot is the tree whose index freshness
198
338
  // and build/test commands are being reported on, which follows the usual scope rules.
199
339
  case 'capabilities': return handleCapabilitiesCommand({ json: o.json, check: o.check, repoRoot: REPO_ROOT });
340
+ case 'enforcement': return handleEnforcementCommand(o);
200
341
  case 'doctor': return handleDoctorCommand({ json: o.json, repoRoot: REPO_ROOT, projectRoot: evidenceRoot(o), adapters: buildAdapterRegistry() });
201
342
  // REPO_ROOT locates the registry (templates ship with the package); stateRoot locates the
202
343
  // caller's evidence, which follows the same scope rules as every other command: -g means
@@ -211,14 +352,14 @@ function dispatchRuntimeCommandInner(o , moment ) {
211
352
  // than from evidence, so the gate had one reachable answer for every task. Forwarding is
212
353
  // all this does: the handler names them back under `callerAsserted` so a stated input is
213
354
  // never mistaken for a measured one.
214
- case 'readiness': return handleReadinessCommand({ taskClass: requireTaskClass(o), taskId: requireTaskId(o), verificationPlan: o.verificationPlan, scopeClear: o.scope, invariants: o.invariants, userDecisionPending: o.userDecisionPending, mode: o.mode, slug: storeSlug, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot });
215
- case 'evidence': return handleEvidenceCommand({ taskId: requireTaskId(o), action: o.action, item: evidenceItemFromFlags(o), batchPath: o.batchPath, evidenceId: o.evidenceId, replacedBy: o.replacedBy, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot });
355
+ case 'readiness': return handleReadinessCommand({ taskClass: requireTaskClass(o), taskId: requireTaskId(o), verificationPlan: o.verificationPlan, scopeClear: o.scope, invariants: o.invariants, userDecisionPending: o.userDecisionPending, mode: o.mode, slug: storeSlug, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot, sourceRoot: evidenceRoot(o) });
356
+ case 'evidence': return handleEvidenceCommand({ taskId: requireTaskId(o), action: o.action, item: evidenceItemFromFlags(o), batchPath: o.batchPath, evidenceId: o.evidenceId, replacedBy: o.replacedBy, stale: o.stale, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot, slug: storeSlug, sourceRoot: evidenceRoot(o), attemptRoot });
216
357
  // Run-ledger views. They resolve their own ledger the way the dispatcher does (nearest
217
358
  // `.doflow` walking up, or the global one) rather than assuming cwd is the project root, so
218
359
  // a view invoked from a subdirectory reads the runs that were actually recorded.
219
360
  // Exit codes follow design §4.2 and are set by the handler itself: 0 = answered, 1 = a
220
361
  // finding the caller must act on.
221
- case 'trace': return handleTraceCommand({ json: o.json, days: o.days, global: o.global, projectRoot: evidenceRoot(o) });
362
+ case 'trace': return handleTraceViewCommand(o);
222
363
  case 'stats': return handleStatsCommand({ json: o.json, days: o.days, global: o.global, projectRoot: evidenceRoot(o) });
223
364
  case 'indicators': return handleIndicatorsCommand({ json: o.json, projectRoot: evidenceRoot(o) });
224
365
  case 'discover': return handleDiscoverCommand({ json: o.json, days: o.days, global: o.global, projectRoot: evidenceRoot(o) });
@@ -229,6 +370,7 @@ function dispatchRuntimeCommandInner(o , moment ) {
229
370
  // the process home directory inside the lifecycle view (design R7) and is not an argument.
230
371
  case 'inventory': return handleInventoryCommand({ json: o.json, global: o.global, targets: o.targets, repoRoot: REPO_ROOT, projectRoot: path.resolve(o.positional[0] || '.') });
231
372
  case 'storage': return handleStorageCommand({ action: o.actionGiven ? o.action : 'report', json: o.json, global: o.global, projectRoot: evidenceRoot(o) });
373
+ case 'config': return handleConfigCommand({ json: o.json, global: o.global, showOrigin: o.showOrigin, positional: o.positional, layer: o.layer, preparedRun: o.preparedRun, repoRoot: REPO_ROOT });
232
374
  // No REPO_ROOT: the scaffold's repo root is the *caller's* repo, reported by the resolver,
233
375
  // because the plan's `files:` paths are relative to it. Passing the DoFlow install here
234
376
  // would detect the wrong language and mirror the wrong tree.
@@ -249,23 +391,47 @@ function dispatchRuntimeCommandInner(o , moment ) {
249
391
  // state and source tree, following the same scope rules as every other command.
250
392
  case 'classify': return handleClassifyCommand({ taskClass: o.taskClass, rationale: o.rationale, proposedBy: o.proposedBy, callingSkill: o.callingSkill, request: o.request, requestFile: o.requestFile, taskId: o.taskId, projectRoot: evidenceRoot(o), json: o.json });
251
393
  case 'workflow': return handleWorkflowCommand({ taskClass: o.taskClass, json: o.json });
252
- case 'orchestrate': return handleOrchestrateCommand({ action: o.action, taskId: o.taskId, taskClass: o.taskClass, stage: o.stage, gate: o.gate, node: o.node, decision: o.decision, note: o.note, reason: o.reason, forced: o.forced, verificationPlan: o.verificationPlan, scope: o.scope, invariants: o.invariants, result: o.result, mcpOutcome: o.mcpOutcome, callingSkill: o.callingSkill, slug: o.slug, json: o.json, repoRoot: REPO_ROOT, stateRoot: evidenceRoot(o), buildAdapters: buildAdapterRegistry });
253
- case 'research-request': return handleResearchRequestCommand({ slug: o.slug, action: o.action === 'status' ? 'list' : o.action, taskId: requireTaskId(o), stageId: o.stageId, question: o.question, reason: o.reason, blocking: o.blocking === undefined ? undefined : o.blocking === 'true' ? true : o.blocking === 'false' ? false : o.blocking, requestId: o.requestId, outcome: o.researchOutcome, claimId: o.claimId, evidenceIds: o.evidenceIds, gap: o.gap, json: o.json, projectRoot: evidenceRoot(o) });
394
+ case 'orchestrate': return handleOrchestrateCommand({ action: o.action, taskId: o.taskId, taskClass: o.taskClass, stage: o.stage, gate: o.gate, node: o.node, decision: o.decision, policy: o.policy, note: o.note, reason: o.reason, forced: o.forced, verificationPlan: o.verificationPlan, scope: o.scope, invariants: o.invariants, result: o.result, mcpOutcome: o.mcpOutcome, callingSkill: o.callingSkill, slug: o.slug, recordSlug: storeSlug, dryRun: o.dryRun, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot, sourceRoot: evidenceRoot(o), buildAdapters: buildAdapterRegistry });
395
+ case 'research-request': return handleResearchRequestCommand({ slug: storeSlug, action: o.action === 'status' ? 'list' : o.action, taskId: requireTaskId(o), stageId: o.stageId, question: o.question, reason: o.reason, blocking: o.blocking === undefined ? undefined : o.blocking === 'true' ? true : o.blocking === 'false' ? false : o.blocking, requestId: o.requestId, outcome: o.researchOutcome, claimId: o.claimId, evidenceIds: o.evidenceIds, gap: o.gap, json: o.json, projectRoot: storeRoot, sourceRoot: evidenceRoot(o) });
254
396
  // The store is shared by every linked worktree of the clone (lifecycle's main-worktree root); the
255
397
  // feature is `--slug`, else the branch's, resolved the way decision and scaffold resolve it.
256
- case 'task-attempt': return handleTaskAttemptCommand({ action: o.actionGiven ? o.action : undefined, slug: o.slug, task: o.task, owner: o.owner, mode: o.mode, token: o.token, result: o.result, evidenceId: o.evidenceId, reason: o.reason, json: o.json, cwd: evidenceRoot(o), resolveSlug: (cwd ) => featureSlugFor({ projectRoot: cwd }) });
257
- case 'retrieve': return handleRetrieveCommand({ query: o.query, top: o.top, json: o.json });
398
+ case 'task-attempt': return handleTaskAttemptCommand({ action: o.actionGiven ? o.action : undefined, slug: o.slug, task: o.task, owner: o.owner, mode: o.mode, token: o.token, result: o.result, evidenceId: o.evidenceId, reason: o.reason, ownerOverride: o.ownerOverride, epoch: o.epoch, reserveTokens: o.reserveTokens, execWorktree: o.execWorktree, tokens: o.tokens, tokensBasis: o.tokensBasis, budgetAttempts: o.budgetAttempts, budgetTokens: o.budgetTokens, by: o.by, ack: o.ack, adoptUnowned: Boolean(o.adoptUnowned), json: o.json, cwd: evidenceRoot(o), resolveSlug: (cwd ) => featureSlugFor({ projectRoot: cwd }) });
399
+ // 081 IC-026: the worker adapter reads and writes the same main-worktree store as task-attempt.
400
+ case 'worker': return handleWorkerCommand({ action: o.actionGiven ? o.action : undefined, slug: o.slug, harness: o.harness, need: o.need, modelCheck: o.modelCheck, budgetTokens: o.budgetTokens, task: o.task, token: o.token, promptFile: o.promptFile, timeoutSeconds: o.timeoutSeconds, wait: Boolean(o.wait), edit: Boolean(o.edit), childOf: o.childOf, request: o.request, execWorktree: o.execWorktree, workerRun: o.workerRun, checks: o.checks, out: o.out, by: o.by, reason: o.reason, json: o.json, env: process.env, cwd: evidenceRoot(o), resolveSlug: (cwd ) => featureSlugFor({ projectRoot: cwd }) });
401
+ // 082 IC-001, IC-038: the dispatch loop shares the main-worktree store root with task-attempt.
402
+ // `--status` is parsed under `attemptStatus` (retrieval-plan's key), so it is renamed here.
403
+ case 'dispatch': return handleDispatchCommand({ action: o.actionGiven ? o.action : undefined, slug: o.slug, by: o.by, reason: o.reason, concurrency: o.concurrency, mode: o.mode, billingCap: o.billingCap, verifyArgv: o.verifyArgv, verifySeconds: o.verifySeconds, attemptSeconds: o.attemptSeconds, reserveTokens: o.reserveTokens, budgetAttempts: o.budgetAttempts, budgetTokens: o.budgetTokens, spawnGrant: o.spawnGrant, harness: o.harness, need: o.need, maxSeconds: o.maxSeconds, dispatch: o.dispatch, status: o.attemptStatus, summary: o.summary, blocker: o.blocker, tokens: o.tokens, tokensBasis: o.tokensBasis, promptFile: o.promptFile, requestId: o.requestId, task: o.task, ack: o.ack, fallback: o.fallback, ladder: o.ladder, json: o.json, env: process.env, cwd: evidenceRoot(o), resolveSlug: (cwd ) => featureSlugFor({ projectRoot: cwd }) });
404
+ // Code scope keeps two roots: the search root is --root or cwd (the invoking checkout), the state root is
405
+ // the feature store above. Guidance gets no stateRoot, exactly as before 079, so it resolves from cwd.
406
+ case 'retrieve': return handleRetrieveCommand({ query: o.query, top: o.top, json: o.json, scope: o.scope,
407
+ profile: o.profile, root: o.root, indexAction: o.indexAction, modelPath: o.modelPath,
408
+ taskId: o.taskId, need: o.need, slug: storeSlug, stateRoot: o.scope === 'code' ? storeRoot : undefined, repoRoot: REPO_ROOT });
258
409
  case 'brief-tools': return handleBriefToolsCommand({ owner: o.owner, tier: o.tier, harness: o.harness, slug: o.slug, headingLevel: o.headingLevel, tasks: o.tasks, json: o.json, repoRoot: REPO_ROOT, projectRoot: storeRoot, buildAdapters: buildAdapterRegistry });
259
410
  case 'advise': return handleAdviseCommand({ action: o.action, actionGiven: o.actionGiven, kind: o.kind, server: o.server, state: o.state, stateFile: o.stateFile, staticAnswer: o.staticAnswer, questionsFile: o.questionsFile, taskId: o.taskId, pickId: o.pickId, actual: o.actual, model: o.model, endpoint: o.endpoint, off: o.off, project: o.project, force: o.force, global: o.global, json: o.json, projectRoot: evidenceRoot(o) });
260
- case 'model-role': return handleModelRoleCommand({ role: o.role, exclude: o.exclude, json: o.json, repoRoot: REPO_ROOT });
411
+ case 'model-role': return handleModelRoleCommand({ role: o.role, exclude: o.exclude, excludeFamilies: o.excludeFamily, json: o.json, repoRoot: REPO_ROOT });
261
412
  case 'route': return handleRouteCommand({ intent: o.intent, query: o.query, check: o.check, json: o.json, projectRoot: evidenceRoot(o) });
262
413
  case 'claim': return handleClaimCommand({ slug: storeSlug, taskId: requireTaskId(o), action: o.action, statement: o.statement, claimId: o.claimId, evidenceId: o.evidenceId, replacedBy: o.replacedBy, relation: o.relation, role: o.role, json: o.json, stateRoot: storeRoot });
263
- case 'context-pack': return handleContextPackCommand({ taskId: requireTaskId(o), taskClass: o.taskClass, objective: o.objective, json: o.json, stateRoot: evidenceRoot(o), slug: o.slug });
264
- case 'retrieval-plan': return handleRetrievalPlanCommand({ slug: o.slug, taskId: requireTaskId(o), action: o.action, need: o.need, stage: o.stage, json: o.json, repoRoot: REPO_ROOT, stateRoot: evidenceRoot(o) });
265
- case 'outcome': return handleOutcomeCommand({ slug: o.slug, taskId: requireTaskId(o), action: o.action, state: o.state, taskClass: o.taskClass, stage: o.stage, readiness: o.readiness, verification: o.verification, json: o.json, repoRoot: REPO_ROOT, stateRoot: evidenceRoot(o) });
414
+ case 'context-pack': return handleContextPackCommand({ taskId: requireTaskId(o), taskClass: o.taskClass, objective: o.objective, json: o.json, stateRoot: storeRoot, sourceRoot: evidenceRoot(o), slug: storeSlug, explicitSlug: o.slug || null, mandatoryFile: o.mandatoryFile, harness: o.harness, maxBytes: o.maxBytes, bare: o.bare, noRecord: o.noRecord, reason: o.reason, since: o.since, expand: o.expand });
415
+ // 078 IC-005 and IC-013: identity is the process environment's alone, never a flag.
416
+ case 'context-emit': return handleContextEmitCommand({ event: o.event, cwd: o.cwd, session: o.session, harness: o.harness, nudge: o.nudge, maxBytes: o.maxBytes, taskId: o.taskId || undefined, slug: o.slug || undefined, stateRoot: redirected ? storeRoot : undefined, peek: o.peek, json: o.json });
417
+ case 'context-grant': return handleContextGrantCommand({ summary: o.summary, toRun: o.toRun, toLineage: o.toLineage, fromWorktree: o.fromWorktree, fromRun: o.fromRun, fromLineage: o.fromLineage, maxBytes: o.maxBytes, json: o.json });
418
+ case 'retrieval-plan': return handleRetrievalPlanCommand({ slug: storeSlug, taskId: requireTaskId(o), action: o.action, need: o.need, stage: o.stage, provider: o.provider, attemptStatus: o.attemptStatus, reason: o.reason, note: o.note, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot, sourceRoot: evidenceRoot(o) });
419
+ case 'outcome': return handleOutcomeCommand({ slug: storeSlug, taskId: requireTaskId(o), action: o.action, state: o.state, taskClass: o.taskClass, stage: o.stage, readiness: o.readiness, verification: o.verification, json: o.json, repoRoot: REPO_ROOT, stateRoot: storeRoot });
266
420
  case 'verify': return handleVerifyCommand({ slug: o.slug, taskId: requireTaskId(o), action: o.action, risk: o.risk, planPath: o.planPath, scope: o.scope, verificationInput: o.verificationInput, verificationPhase: o.verificationPhase, baselineRef: o.baselineRef, json: o.json, projectRoot: evidenceRoot(o) });
267
421
  case 'leak-scan': return handleLeakScanCommand({ paths: o.paths, exclude: o.exclude, json: o.json, repoRoot: evidenceRoot(o) });
268
422
  case 'recover': return handleRecoverCommand({ errorMessage: o.errorMessage, failedChecks: o.failedChecks, iteration: o.iteration, agent: o.agent, json: o.json });
423
+ case 'roadmap': return handleRoadmapVerbCommand(o);
424
+ case 'plan-check': return handlePlanCheckCommand({ positional: o.positional, extraFlags: o.extraFlags, json: o.json, cwd: process.cwd() });
425
+ // One --path only: a second occurrence reaches the handler as a list, which it refuses as usage.
426
+ case 'review': return handleReviewCommand({ action: o.actionGiven ? o.action : undefined, slug: o.slug, path: o.paths?.length === 1 ? o.paths[0] : o.paths, base: o.base, preset: o.preset, json: o.json, cwd: process.cwd(), env: process.env });
427
+ case 'protocol': return handleProtocolCommand({ action: o.actionGiven ? o.action : undefined, kind: o.kind, taskClass: o.taskClass, risk: o.risk, preset: o.preset, positional: o.positional, json: o.json, cwd: process.cwd() });
428
+ case 'rules': return handleRulesCommand({ taskClass: o.taskClass, json: o.json, global: o.global, positional: o.positional, env: process.env, cwd: process.cwd() });
429
+ case 'primary': return handlePrimaryCommand({ action: o.actionGiven ? o.action : undefined, json: o.json, flags: o.flagsGiven, env: process.env, cwd: process.cwd() });
430
+ case 'package': return handlePackageCommand({ action: o.actionGiven ? o.action : undefined, path: o.paths?.length === 1 ? o.paths[0] : o.paths, id: o.id, source: o.source, index: o.index, packageVersion: o.packageVersion, plan: o.planId, repair: o.repair, points: o.points, dir: o.dir, json: o.json });
431
+ case 'slot': return handleSlotCommand({ action: o.actionGiven ? o.action : undefined, name: o.name, label: o.label, timeout: o.timeout, noWait: Boolean(o.noWait), command: o.slotCommand, by: o.by, reason: o.reason, positional: o.positional, extraFlags: o.extraFlags, json: o.json, env: process.env, cwd: process.cwd() });
432
+ case 'fleet': return handleFleetCommand({ action: o.actionGiven ? o.action : undefined, json: o.json, flags: o.flagsGiven, env: process.env, cwd: process.cwd() });
433
+ case 'transcript': return handleTranscriptCommand({ action: o.actionGiven ? o.action : undefined, agent: o.agent, cursor: o.cursor, limit: o.limit, window: o.window, json: o.json, flags: o.flagsGiven, env: process.env, cwd: process.cwd() });
434
+ case 'models': return handleModelsCommand({ action: o.actionGiven ? o.action : (o.positional?.[0] || 'list'), positional: o.positional, harness: o.harness, model: o.model, json: o.json, projectRoot: evidenceRoot(o) });
269
435
  default: console.error(`doflow: unknown command '${o.cmd}'`); process.exit(1);
270
436
  }
271
437
  }
@@ -274,8 +440,33 @@ function dispatchRuntimeCommandInner(o , moment ) {
274
440
  async function dispatchRuntimeCommand(o ) {
275
441
  const moment = {};
276
442
  const result = await dispatchRuntimeCommandInner(o, moment);
277
- if (o.cmd !== 'storage') upkeepAfterVerb({ projectRoot: moment.projectRoot });
443
+ if (runsUpkeep(o)) upkeepAfterVerb({ projectRoot: moment.projectRoot });
278
444
  return result;
279
445
  }
280
446
 
447
+ function handleEnforcementCommand(o ) {
448
+ const fail = (error , detail ) => {
449
+ if (o.json) console.log(JSON.stringify({ error, detail }));
450
+ else console.error(`doflow enforcement: ${detail}`);
451
+ process.exitCode = 2;
452
+ return 2;
453
+ };
454
+ if (typeof o.harness !== 'string' || !o.harness || typeof o.policy !== 'string' || !o.policy || (o.profile !== undefined && typeof o.profile !== 'string')) return fail('usage', 'requires --harness <id> --policy <id> [--profile <id>]');
455
+ try {
456
+ const registry = loadRegistry({ repoRoot: REPO_ROOT });
457
+ const answer = enforcementFor(registry.coverage, { harness: o.harness, policy: o.policy, ...(o.profile ? { profile: o.profile } : {}) });
458
+ if (o.json) console.log(JSON.stringify(answer));
459
+ else {
460
+ console.log(`${answer.harness} ${answer.policy}: ${answer.enforced ? 'enforced' : 'not enforced'} (${answer.class}, ${answer.verification})`);
461
+ for (const item of answer.doesNotCover) console.log(`does not cover: ${item}`);
462
+ for (const item of answer.prerequisites) console.log(`prerequisite: ${item}`);
463
+ }
464
+ process.exitCode = answer.enforced ? 0 : 1;
465
+ return process.exitCode;
466
+ } catch (error ) {
467
+ if (error instanceof UnknownEnforcementTarget) return fail(error.code, error.message);
468
+ return fail('usage', error.message);
469
+ }
470
+ }
471
+
281
472
  module.exports = { dispatchRuntimeCommand };
package/src/cli/shared.js CHANGED
@@ -20,7 +20,6 @@ const { createAdapterRegistry } = require('../adapters/index.js');
20
20
  const { declaredHarnessPaths } = require('../helper/harness-paths.js');
21
21
  const claudeAdapter = require('../adapters/claude/index.js');
22
22
  const codexAdapter = require('../adapters/codex/index.js');
23
- const { createGeminiAdapter } = require('../adapters/gemini/index.js');
24
23
  const { createOpenCodeAdapter } = require('../adapters/opencode/index.js');
25
24
  const { createPiAdapter } = require('../adapters/pi/index.js');
26
25
  const { createCopilotAdapter } = require('../adapters/copilot/index.js');
@@ -55,7 +54,6 @@ function buildAdapterRegistry() {
55
54
  return createAdapterRegistry({
56
55
  claude: claudeAdapter.createClaudeAdapter({ declaredPaths: declared.claude }),
57
56
  codex: codexAdapter.createCodexAdapter({ declaredPaths: declared.codex }),
58
- gemini: createGeminiAdapter({ declaredPaths: declared.gemini }),
59
57
  opencode: createOpenCodeAdapter({ declaredPaths: declared.opencode }),
60
58
  pi: createPiAdapter({ declaredPaths: declared.pi }),
61
59
  copilot: createCopilotAdapter({ declaredPaths: declared.copilot }),
package/src/helper/git.js CHANGED
@@ -5,7 +5,7 @@
5
5
  //
6
6
  // First module of the TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md). Authored as `.ts` and
7
7
  // run directly by Node's native type-stripping (process.features.typescript === 'strip', Node
8
- // ^22.18.0 || >=24.3.0), so no build step and no runtime dependency are added. The body stays CommonJS
8
+ // >=24.22.1), so no build step and no runtime dependency are added. The body stays CommonJS
9
9
  // (`module.exports`) so a `require('../helper/git.js')` from a `.js` caller keeps working while the
10
10
  // rest of the tree is still `.js`; the explicit `.ts` extension on those requires is what the
11
11
  // CommonJS resolver needs to find this file.
@@ -10,7 +10,7 @@
10
10
  // the inner command glob is what every other harness wants.
11
11
  //
12
12
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
13
- // native type-stripping (process.features.typescript === 'strip', Node ^22.18.0 || >=24.3.0) — no build step and
13
+ // native type-stripping (process.features.typescript === 'strip', Node >=24.22.1) — no build step and
14
14
  // no runtime dependency. The body stays CommonJS so a `.js` caller's `require('../helper/guardrails.js')`
15
15
  // keeps working while the rest of the tree is still `.js`; the explicit `.ts` extension is what the
16
16
  // CommonJS resolver needs to find this file.
@@ -16,9 +16,12 @@ const MARKER_END = '<!-- doflow:end -->';
16
16
  /**
17
17
  * Merge `srcAbs`'s content into `dstAbs` inside a doflow-owned marker span,
18
18
  * preserving any of `dstAbs`'s own content outside that span byte-for-byte.
19
+ * With `content`, that text is merged instead of the file `srcAbs` names.
19
20
  */
20
- function mergeMarkedSection(srcAbs , dstAbs , { dryRun = false } = {}) {
21
- const srcContent = fs.readFileSync(srcAbs, 'utf8').replace(/\s+$/, '');
21
+ function mergeMarkedSection(srcAbs , dstAbs , { dryRun = false, content } = {}) {
22
+ // `content` is already-rendered text (a pointer with its per-scope path filled in); it replaces
23
+ // reading `srcAbs`, so a caller never has to stage a temporary source file.
24
+ const srcContent = (content !== undefined ? content : fs.readFileSync(srcAbs, 'utf8')).replace(/\s+$/, '');
22
25
  const section = `${MARKER_START}\n${srcContent}\n${MARKER_END}\n`;
23
26
 
24
27
  // Read once, reused both for parsing and for the final no-op comparison below.
@@ -81,28 +84,43 @@ function mergeMarkedSection(srcAbs , dstAbs , { dryRun = false }
81
84
  return { changed: true };
82
85
  }
83
86
 
87
+ /**
88
+ * Where `text`'s doflow span is: the first of `startMarkers`, the first MARKER_END after it, and no
89
+ * start marker after that end (a second span is `malformed`). `after` is past the end marker and the
90
+ * one '\n' that follows it.
91
+ */
92
+ function markedSpan(text , startMarkers = [MARKER_START]) {
93
+ const found = startMarkers.map((marker) => ({ at: text.indexOf(marker), length: marker.length })).filter((item) => item.at !== -1);
94
+ if (!found.length) return { state: 'none' };
95
+ const first = found.reduce((a, b) => (b.at < a.at ? b : a));
96
+ const end = text.indexOf(MARKER_END, first.at + first.length);
97
+ if (end === -1) return { state: 'malformed' };
98
+ let after = end + MARKER_END.length;
99
+ if (startMarkers.some((marker) => text.indexOf(marker, after) !== -1)) return { state: 'malformed' };
100
+ if (text[after] === '\n') after += 1;
101
+ return { state: 'ok', start: first.at, after };
102
+ }
103
+
84
104
  /**
85
105
  * Strip only the doflow-managed span from `file`, leaving any surrounding content (before/after
86
106
  * the markers) byte-for-byte untouched. Companion to `mergeMarkedSection` — shared by the Claude
87
107
  * and Codex adapters' `remove()`, which had each carried an identical private copy of this.
88
108
  * A file that held nothing but the span is DoFlow's own and is deleted; any other file is written
89
- * back, even when only whitespace remains.
109
+ * back, even when only whitespace remains. `startMarkers` (default: MARKER_START alone) lets a
110
+ * caller also accept an older start marker; the retirement sweep is the only one that does.
90
111
  * @returns whether a managed span was found and stripped (false if the file doesn't
91
112
  * exist or has no doflow markers — both are no-ops, not errors)
92
113
  */
93
- function removeMarkedSection(file ) {
114
+ function removeMarkedSection(file , { startMarkers = [MARKER_START] } = {}) {
94
115
  if (!fs.existsSync(file)) return false;
95
116
  const existing = fs.readFileSync(file, 'utf8');
96
- const start = existing.indexOf(MARKER_START);
97
- if (start === -1) return false;
98
- const end = existing.indexOf(MARKER_END, start + MARKER_START.length);
99
- if (end === -1 || existing.indexOf(MARKER_START, end + MARKER_END.length) !== -1) {
117
+ const span = markedSpan(existing, startMarkers);
118
+ if (span.state === 'none') return false;
119
+ if (span.state === 'malformed') {
100
120
  throw new Error(`Refusing to remove malformed DoFlow markers in ${file}`);
101
121
  }
102
- let after = end + MARKER_END.length;
103
- if (existing[after] === '\n') after += 1;
104
- const before = existing.slice(0, start);
105
- const rest = existing.slice(after);
122
+ const before = existing.slice(0, span.start);
123
+ const rest = existing.slice(span.after);
106
124
  if (before === '' && rest === '') {
107
125
  fs.unlinkSync(file);
108
126
  return true;
@@ -113,4 +131,4 @@ function removeMarkedSection(file ) {
113
131
  return true;
114
132
  }
115
133
 
116
- module.exports = { mergeMarkedSection, removeMarkedSection, MARKER_START, MARKER_END };
134
+ module.exports = { mergeMarkedSection, removeMarkedSection, markedSpan, MARKER_START, MARKER_END };
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ // Package, local, qualified and projected identifiers (122 IC-003, PN-3). Pure and dependency-free so
4
+ // `src/registry/` (row 120's worker ids) and `src/runtime/` import the one definition.
5
+
6
+ const PACKAGE_ID = /^[a-z][a-z0-9-]{0,14}[a-z0-9]$/;
7
+ const LOCAL_ID = /^[a-z][a-z0-9-]{0,13}[a-z0-9]$/;
8
+ const QUALIFIED_MAX = 32;
9
+ // Refused as a package id (reserved-id); `doflow` is usable only by the bundled first-party package.
10
+ const RESERVED = ['doflow', 'do', 'core', 'user', 'project', 'local', 'env', 'auto', 'package', 'packages'];
11
+
12
+ const isPackageId = (value ) => typeof value === 'string' && PACKAGE_ID.test(value) && !value.includes('--');
13
+ const isLocalId = (value ) => typeof value === 'string' && LOCAL_ID.test(value) && !value.includes('--');
14
+
15
+ /** True for a package id the grammar accepts but IC-003 reserves: the listed names and any `do-` or `doflow-` prefix. */
16
+ function isReservedPackageId(value ) {
17
+ return typeof value === 'string' && (RESERVED.includes(value) || value.startsWith('do-') || value.startsWith('doflow-'));
18
+ }
19
+
20
+ function isQualifiedId(value ) {
21
+ if (typeof value !== 'string' || value.length > QUALIFIED_MAX) return false;
22
+ const parts = value.split('.');
23
+ return parts.length === 2 && isPackageId(parts[0]) && isLocalId(parts[1]);
24
+ }
25
+
26
+ /** `<package id>.<local id>`, or null when either part breaks its grammar. */
27
+ function qualifiedId(packageId , localId ) {
28
+ const joined = `${packageId}.${localId}`;
29
+ return isPackageId(packageId) && isLocalId(localId) && joined.length <= QUALIFIED_MAX ? joined : null;
30
+ }
31
+
32
+ /** The harness-file name of a qualified id, `<package id>-<local id>`; null when the input is not a qualified id. */
33
+ function projectedName(qualified ) {
34
+ return isQualifiedId(qualified) ? qualified.replace('.', '-') : null;
35
+ }
36
+
37
+ module.exports = { isPackageId, isLocalId, isQualifiedId, isReservedPackageId, qualifiedId, projectedName };