@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
@@ -0,0 +1,677 @@
1
+ 'use strict';
2
+
3
+ // Feature 081 C.3 (IC-011, IC-017 view, IC-019, IC-025; DEC-005, DEC-025, DEC-032, DEC-036,
4
+ // DEC-038, DEC-042, DEC-054): the `worker` verb's handler.
5
+ //
6
+ // `handleWorkerCommand` validates IC-025's flags per action (a flag on an action that does not take
7
+ // it is exit 2 `usage`), runs the action and maps its disposition to the exit contract: 0 for
8
+ // `discovered`, `probed`, `described`, `selected`, `started`, `cancelled`, `status` and `recorded`; 1 with the
9
+ // JSON for `refused` and `none-eligible`; 2 with `{ ok: false, error }` for `usage`,
10
+ // `store-unreadable` and `registry-invalid`.
11
+ //
12
+ // Run is IC-011: steps 0 to 11 read without a lock, step 12 inserts the run inside the run store's
13
+ // lock, then the detached supervisor is spawned with the caller's own environment and cwd, and its
14
+ // pid and start are written by compare-and-set. The record carries the full worker block as
15
+ // `descriptor` (DEC-054), so the supervisor launches with no registry and tests inject descriptors
16
+ // in process (DEC-036). The 080 ledger is only read: run never claims, finishes, releases or reports
17
+ // usage (DEC-005). Status is IC-019 and writes nothing (DEC-025); cancel is C.1's `cancelRun` with
18
+ // this process's own `env` and `cwd` (DEC-038).
19
+ //
20
+ // 082 amends run in two ways. `--edit` (IC-014, DEC-020, DEC-021, DEC-048): after step 8 the
21
+ // execution worktree must be a dispatch attempt worktree on its `doflow-attempt/<slug>/<d-id>`
22
+ // branch whose `.doflow-worktree-base` is an ancestor of its HEAD; step 11 uses the edit baseline;
23
+ // the record gains `edit: { base, branch, callerTop }`; the supervisor launches the edit recipe and
24
+ // records `isolation`. `--child-of <w-id>` (IC-015, DEC-028, DEC-041, DEC-062): a read-only child
25
+ // of a live granter run on the same `(task, token)`, in its own `<granter d-id>.c<n>` worktree,
26
+ // always run-scoped, unique on `(task, token, childOf, request)`; `--request` must be the child
27
+ // worktree's basename and, under a dispatch run, a `requested` spawn request of the granter's
28
+ // dispatch (read through dispatch/store.ts's `readDispatch`); status and cancel by
29
+ // `--task --token` keep naming the granter, and cancelling a child never cancels the 080 attempt.
30
+ //
31
+ // This module makes no fs write call: the run store is written through `insertRun` and
32
+ // `transition`, the probe record by readiness, the ledger only by 080's cancel (G26 (b), IC-027).
33
+
34
+ const fs = require('node:fs');
35
+ const path = require('node:path');
36
+ const crypto = require('node:crypto');
37
+ const { spawn, spawnSync } = require('node:child_process');
38
+ const readiness = require('./readiness.js');
39
+ const { activeWorkers, workerGate } = require('./active.js');
40
+ const { readBindings } = require('./models.js');
41
+ const { workerCatalogue } = require('./catalogue.js');
42
+ const { storeFileFor, newRunId, insertRun, transition, readRuns, RUN_ID } = require('./run-store.js');
43
+ const evidence = require('./evidence.js');
44
+ const { launchEnv, launchRecord, PROMPT_LIMITS } = require('./launch.js');
45
+ const { BASE_FILE } = require('../worktree.js');
46
+ const { SUPERVISOR_FILE, resultOfRun } = require('./supervisor.js');
47
+ const { cancelRun } = require('./cancel.js');
48
+ const processTree = require('./process-tree.js');
49
+ const attempts = require('../task-attempts.js');
50
+ const { runKeyFor, identityRefusal } = require('../task-run.js');
51
+ const { readTaskState } = require('../task-state.js');
52
+ const { findWorkTreeTop } = require('../source-version.js');
53
+ const { projectRoot: lifecycleProjectRoot } = require('../lifecycle/root.js');
54
+ const { isSafeSlug } = require('../task-scope.js');
55
+ const { loadRegistry } = require('../../registry/index.js');
56
+ const { REPO_ROOT } = require('../../helper/repo-root.js');
57
+ const { readDispatch, storeFileFor: dispatchStoreFileFor } = require('../dispatch/store.js');
58
+
59
+ const ACTIONS = Object.freeze(['discover', 'probe', 'describe', 'select', 'run', 'status', 'cancel', 'evidence']);
60
+ /** IC-025: the dispositions that exit 0; `refused` and `none-eligible` exit 1. */
61
+ const EXIT_ZERO = Object.freeze(['discovered', 'probed', 'described', 'selected', 'started', 'cancelled', 'status', 'recorded']);
62
+ const ERROR_CODES = Object.freeze(['usage', 'store-unreadable', 'registry-invalid']);
63
+ const TASK_ID = /^[A-Za-z]+\.\d+$/;
64
+ const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._ -]*$/;
65
+ const DIGITS = /^[0-9]+$/;
66
+ /** 078 `ATTEMPT_PATTERN`'s ceiling (IC-011 step 6). */
67
+ const TOKEN_MAX = 9999;
68
+ const TIMEOUT_DEFAULT_SECONDS = 1800;
69
+ const TIMEOUT_MAX_SECONDS = 14400;
70
+ /** IC-011: how long run waits for the record to leave `starting`, and the poll intervals. */
71
+ const START_WAIT_MS = 10000;
72
+ const START_POLL_MS = 100;
73
+ const WAIT_POLL_MS = 500;
74
+ const WAIT_GRACE_MS = 10000;
75
+ /** 082 IC-014 / IC-015: where dispatch worktrees live, an attempt's name and a spawn request id. */
76
+ const DISPATCH_WORKTREES = path.join('.doflow', 'state', 'dispatch-worktrees');
77
+ const ATTEMPT_NAME = /^d-[0-9a-f]{12}$/;
78
+ const CHILD_NAME = /^(d-[0-9a-f]{12})\.c([1-9][0-9]*)$/;
79
+ const SHA = /^[0-9a-f]{40,64}$/;
80
+ const GIT_READ_TIMEOUT_MS = 10000;
81
+ const REPO_ENV = Object.freeze(['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_COMMON_DIR', 'GIT_OBJECT_DIRECTORY']);
82
+
83
+ /** IC-025: the actions each flag may be given to; any other action is exit 2 `usage`. */
84
+ const FLAG_ACTIONS = Object.freeze({
85
+ slug: { flag: '--slug', actions: ['run', 'status', 'cancel', 'evidence'] },
86
+ harness: { flag: '--harness', actions: ['probe', 'describe', 'select', 'run', 'evidence'] },
87
+ edit: { flag: '--edit', actions: ['describe', 'select', 'run'] },
88
+ childOf: { flag: '--child-of', actions: ['run'] },
89
+ request: { flag: '--request', actions: ['run'] },
90
+ execWorktree: { flag: '--exec-worktree', actions: ['run'] },
91
+ need: { flag: '--need', actions: ['probe', 'describe', 'select', 'run'] },
92
+ modelCheck: { flag: '--model-check', actions: ['probe'] },
93
+ budgetTokens: { flag: '--budget-tokens', actions: ['probe'] },
94
+ task: { flag: '--task', actions: ['run', 'status', 'cancel'] },
95
+ token: { flag: '--token', actions: ['run', 'status', 'cancel'] },
96
+ promptFile: { flag: '--prompt-file', actions: ['run'] },
97
+ timeoutSeconds: { flag: '--timeout-seconds', actions: ['run'] },
98
+ wait: { flag: '--wait', actions: ['run'] },
99
+ workerRun: { flag: '--worker-run', actions: ['status', 'cancel', 'evidence'] },
100
+ checks: { flag: '--checks', actions: ['evidence'] },
101
+ out: { flag: '--out', actions: ['evidence'] },
102
+ by: { flag: '--by', actions: ['cancel'] },
103
+ reason: { flag: '--reason', actions: ['cancel'] },
104
+ });
105
+
106
+ /** DEC-063: the execArgv entries the supervisor spawn forwards; every other entry is dropped. */
107
+ const EXEC_ARGV_ALLOWED = new Set(['--experimental-strip-types', '--experimental-transform-types', '--no-experimental-strip-types', '--no-warnings']);
108
+ const DISABLE_WARNING = /^--disable-warning=.+$/;
109
+
110
+ /**
111
+ * The type-stripping and warning flags of `execArgv`, element by element. A caller's `-e`, `-p`,
112
+ * `--inspect`, `--watch` or `--test` would otherwise strand the detached supervisor (DEC-063); a
113
+ * two-element `--disable-warning <x>` is dropped whole, so its value is never read as the script.
114
+ */
115
+ function allowedExecArgv(execArgv ) {
116
+ return execArgv.filter((arg) => EXEC_ARGV_ALLOWED.has(arg) || DISABLE_WARNING.test(arg));
117
+ }
118
+
119
+ /** A caller or environment failure: exit 2 with `{ ok: false, error }`. */
120
+ class WorkerCommandError extends Error {
121
+
122
+ constructor(code , message ) {
123
+ super(message);
124
+ this.name = 'WorkerCommandError';
125
+ this.code = code;
126
+ }
127
+ }
128
+
129
+ const usage = (message ) => new WorkerCommandError('usage', message);
130
+ const given = (value ) => value !== undefined && value !== null && value !== false;
131
+ const defaultSleep = (ms ) => new Promise((resolve) => setTimeout(resolve, ms));
132
+
133
+ function refused(reason , extra = {}) {
134
+ return { disposition: 'refused', reason, ...extra };
135
+ }
136
+
137
+ function real(target ) {
138
+ try { return fs.realpathSync(target); } catch { return path.resolve(target); }
139
+ }
140
+
141
+ /** A whole number from digits only, within `[min, max]`, or undefined when not given. */
142
+ function digits(value , flag , min , max = Number.MAX_SAFE_INTEGER) {
143
+ if (!given(value)) return undefined;
144
+ const text = String(value);
145
+ if (!DIGITS.test(text) || Number(text) < min || Number(text) > max) {
146
+ throw usage(`${flag} expects a whole number from ${min} to ${max}, got '${value}'`);
147
+ }
148
+ return Number(text);
149
+ }
150
+
151
+ /** `--need` repeatable or comma-separated. */
152
+ function needList(need ) {
153
+ if (!given(need)) return [];
154
+ const items = Array.isArray(need) ? need : [need];
155
+ return items.flatMap((item) => String(item).split(',')).map((item) => item.trim()).filter(Boolean);
156
+ }
157
+
158
+ function refuseMisplacedFlags(action , flags ) {
159
+ for (const [key, { flag, actions }] of Object.entries(FLAG_ACTIONS)) {
160
+ if (given(flags[key]) && !actions.includes(action)) throw usage(`${flag} does not apply to --action ${action}`);
161
+ }
162
+ }
163
+
164
+ /** The registry, loaded once; a validation failure is `registry-invalid`. */
165
+ function registryOf(registry ) {
166
+ if (registry) return registry;
167
+ try {
168
+ return loadRegistry({ repoRoot: REPO_ROOT });
169
+ } catch (error ) {
170
+ throw new WorkerCommandError('registry-invalid', error?.message || String(error));
171
+ }
172
+ }
173
+
174
+ /** The worker block for `harness`: the injected descriptor, or the registry's. */
175
+ function workerBlock(reg , descriptors , harness ) {
176
+ const entries = workerCatalogue({ registry: reg });
177
+ const ids = entries.map((entry ) => entry.id);
178
+ if (!ids.includes(harness)) throw usage(`unknown harness '${harness}'; known: ${ids.join(', ')}`);
179
+ if (descriptors) {
180
+ if (!Object.hasOwn(descriptors, harness)) throw usage(`no worker descriptor was given for harness '${harness}'`);
181
+ return descriptors[harness];
182
+ }
183
+ return entries.find((entry ) => entry.id === harness).worker;
184
+ }
185
+
186
+ /** `--slug`, or the feature resolved from the branch. */
187
+ function featureOf(slug , resolveSlug , cwd ) {
188
+ const feature = slug || (resolveSlug ? resolveSlug(cwd) : null);
189
+ if (!feature) throw usage('--slug is required: no feature could be resolved from the current branch');
190
+ if (!isSafeSlug(feature)) throw usage(`'${feature}' is not a valid feature slug`);
191
+ return feature;
192
+ }
193
+
194
+ /** IC-011 step 0's `--prompt-file`: a readable regular file within IC-003's limit for the recipe. */
195
+ function readPrompt(promptFile , cwd , worker ) {
196
+ const file = path.resolve(cwd, String(promptFile));
197
+ let bytes ;
198
+ try {
199
+ if (!fs.statSync(file).isFile()) throw new Error('not a regular file');
200
+ bytes = fs.readFileSync(file);
201
+ } catch (error ) {
202
+ throw usage(`--prompt-file '${promptFile}' is not a readable regular file: ${error?.code || error?.message}`);
203
+ }
204
+ const mode = worker?.oneShot?.promptVia === 'stdin' ? 'stdin' : 'argv';
205
+ if (bytes.length > PROMPT_LIMITS[mode]) {
206
+ throw usage(`--prompt-file is ${bytes.length} bytes; ${mode} mode allows ${PROMPT_LIMITS[mode]}`);
207
+ }
208
+ return { path: file, bytes, sha256: crypto.createHash('sha256').update(bytes).digest('hex') };
209
+ }
210
+
211
+ /** The 080 ledger of `slug`, read only. */
212
+ function ledgerOf(root , slug ) {
213
+ return readTaskState(fs, attempts.storeFile(root, slug));
214
+ }
215
+
216
+ function attemptFor(ledger , task , token ) {
217
+ const list = ledger?.tasks?.[task]?.attempts;
218
+ return Array.isArray(list) ? list.find((a ) => a.token === token) ?? null : null;
219
+ }
220
+
221
+ /** One bounded, read-only git call in `cwd` with the repository variables dropped. */
222
+ function gitRead(cwd , args , env ) {
223
+ const clean = { ...env, GIT_OPTIONAL_LOCKS: '0' };
224
+ for (const k of REPO_ENV) delete clean[k];
225
+ const r = spawnSync('git', ['-C', cwd, ...args], { env: clean, timeout: GIT_READ_TIMEOUT_MS, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
226
+ return { status: r.error ? null : r.status, stdout: r.stdout || '' };
227
+ }
228
+
229
+ /**
230
+ * 082 IC-014 after step 8: `execWorktree` is `<root>/.doflow/state/dispatch-worktrees/d-<12 hex>`
231
+ * on `refs/heads/doflow-attempt/<slug>/<d-id>`, and its base file names an ancestor of HEAD.
232
+ * Returns `{ reason }` for a refusal, else `{ base, branch }`.
233
+ */
234
+ function attemptWorktree(root , slug , execWorktree , env ) {
235
+ const dir = real(execWorktree);
236
+ const id = path.basename(dir);
237
+ if (path.dirname(dir) !== path.join(real(root), DISPATCH_WORKTREES) || !ATTEMPT_NAME.test(id)) return { reason: 'exec-worktree-not-attempt' };
238
+ const branch = `refs/heads/doflow-attempt/${slug}/${id}`;
239
+ const ref = gitRead(dir, ['symbolic-ref', '-q', 'HEAD'], env);
240
+ if (ref.status !== 0 || ref.stdout.trim() !== branch) return { reason: 'exec-worktree-not-attempt' };
241
+ let base = '';
242
+ try { base = fs.readFileSync(path.join(dir, BASE_FILE), 'utf8').trim(); } catch { base = ''; }
243
+ if (!SHA.test(base) || gitRead(dir, ['merge-base', '--is-ancestor', base, 'HEAD'], env).status !== 0) return { reason: 'attempt-base-unreadable' };
244
+ return { base, branch };
245
+ }
246
+
247
+ /** 082 IC-015 step 8: `execWorktree` is `<root>/.doflow/state/dispatch-worktrees/<granter d-id>.c<n>`. */
248
+ function childWorktreeOk(root , granter , execWorktree ) {
249
+ const home = path.join(real(root), DISPATCH_WORKTREES);
250
+ const dir = real(execWorktree);
251
+ const granterDir = typeof granter.execWorktree === 'string' ? real(granter.execWorktree) : '';
252
+ const match = CHILD_NAME.exec(path.basename(dir));
253
+ return Boolean(match) && path.dirname(dir) === home && path.dirname(granterDir) === home
254
+ && ATTEMPT_NAME.test(path.basename(granterDir)) && match [1] === path.basename(granterDir) && dir !== granterDir;
255
+ }
256
+
257
+ function isDirectory(target ) {
258
+ try { return typeof target === 'string' && fs.statSync(target).isDirectory(); } catch { return false; }
259
+ }
260
+
261
+ /**
262
+ * 082 IC-015: `--request` names the child worktree (its basename) and, when the slug has a dispatch
263
+ * run, a `requested` spawn request of the dispatch whose worker run is the granter. Returns the
264
+ * refusal reason, or null. Reads the dispatch store without a lock; an unreadable store throws
265
+ * `store-unreadable`. With no dispatch run there is no grant to name, so only the basename rule
266
+ * applies (081-level children, `worker-child-run.test.js`).
267
+ */
268
+ function childRequestRefusal(root , slug , childOf , request , execWorktree ) {
269
+ if (path.basename(real(execWorktree)) !== request) return 'request-mismatch';
270
+ const dispatch = readDispatch(dispatchStoreFileFor(root, slug));
271
+ if (!dispatch.run) return null;
272
+ const granter = (Object.values(dispatch.dispatches) ).find((d) => d.workerRunId === childOf && d.phase !== 'ended');
273
+ const r = Object.prototype.hasOwnProperty.call(dispatch.spawnRequests, request) ? dispatch.spawnRequests[request] : null;
274
+ if (!granter || !r || r.state !== 'requested' || r.granterDispatchId !== granter.dispatchId) return 'unknown-request';
275
+ return null;
276
+ }
277
+
278
+ /** IC-011 step 11: discover for the harness, then IC-008 on its effective facts. Writes nothing.
279
+ * `edit` swaps in the edit baseline (082 IC-013). */
280
+ function evaluate({ reg, worker, harness, needs, coverage, env, cwd, root, nowMs, platform, edit = false } ) {
281
+ const found = readiness.discover({ descriptors: { [harness]: worker }, registry: reg, env, cwd, now: nowMs, platform }).harnesses[0];
282
+ const facts = readiness.effectiveFacts(
283
+ readiness.readProbeRecord(root, harness),
284
+ { ...found, descriptorDigest: readiness.descriptorDigest(worker) },
285
+ nowMs,
286
+ );
287
+ const requirements = readiness.requirementsFor({ harness, worker }, facts, needs, coverage, edit);
288
+ return { requirements, version: found.version, ...readiness.eligibility(requirements) };
289
+ }
290
+
291
+ /** IC-011 steps 0 to 12, then the supervisor spawn and the start poll. */
292
+ async function runAction(o ) {
293
+ const { env, cwd, root, platform, clock, sleep, slug, harness, task, token, needs, timeoutSeconds, prompt, worker, reg, coverage } = o;
294
+ const binding = o.binding ?? null;
295
+ const edit = o.edit === true;
296
+ const childOf = o.childOf ?? null;
297
+ const nowMs = () => clock().getTime();
298
+
299
+ // Step 1: the caller's identity, so a run is never started whose 080 cancel would be refused.
300
+ const key = runKeyFor(env, cwd);
301
+ const who = identityRefusal(key.identity);
302
+ if (who) return refused(who.reason, { runKey: key.runKey });
303
+
304
+ // Steps 2 to 7: the 080 ledger, read only.
305
+ const ledger = ledgerOf(root, slug);
306
+ const active = Array.isArray(ledger?.tasks?.[task]?.attempts) ? ledger.tasks[task].attempts.find((a ) => a.state === 'active') : null;
307
+ if (!active) return refused('no-active-attempt', { task });
308
+ if (active.token !== token) return refused('token-mismatch', { task, token, activeToken: active.token });
309
+ if (active.runKey !== key.runKey) return refused('foreign-run', { task, token, runKey: active.runKey ?? null });
310
+ if (token > TOKEN_MAX) return refused('token-out-of-range', { task, token });
311
+ if (active.mode !== 'subagent') return refused('mode-mismatch', { task, token, mode: active.mode ?? null });
312
+
313
+ // 082 IC-015 after step 7: a live, top-level granter on the same (task, token).
314
+ const file = storeFileFor(root, slug);
315
+ let granter = null;
316
+ if (childOf) {
317
+ granter = readRuns(file)[childOf] ?? null;
318
+ if (granter && (granter.childOf ?? null) !== null) return refused('depth-exceeded', { task, token, childOf });
319
+ if (!granter || granter.phase === 'ended' || granter.end || granter.task !== task || granter.token !== token) {
320
+ return refused('granter-mismatch', { task, token, childOf });
321
+ }
322
+ }
323
+
324
+ // Step 8: the execution worktree exists; a child's is `--exec-worktree` (082 IC-015).
325
+ const execWorktree = childOf ? path.resolve(cwd, String(o.execWorktree)) : active.execWorktree;
326
+ if (!isDirectory(execWorktree)) return refused('exec-worktree-missing', { task, token, execWorktree: execWorktree ?? null });
327
+ if (childOf && !childWorktreeOk(root, granter, execWorktree)) return refused('exec-worktree-not-child', { task, token, execWorktree });
328
+ if (childOf) {
329
+ const requestRefusal = childRequestRefusal(root, slug, childOf, String(o.request), execWorktree);
330
+ if (requestRefusal) return refused(requestRefusal, { task, token, childOf, request: o.request });
331
+ }
332
+
333
+ // 082 IC-014 after step 8: an edit run's worktree is a dispatch attempt worktree.
334
+ let editRecord = null;
335
+ if (edit) {
336
+ const attempt = attemptWorktree(root, slug, execWorktree, env);
337
+ if (attempt.reason) return refused(attempt.reason, { task, token, execWorktree });
338
+ editRecord = { base: attempt.base, branch: attempt.branch, callerTop: key.top };
339
+ }
340
+
341
+ // Step 9: the caller is not the unit this worker will be.
342
+ if (key.identity.lineageKind === 'launched' && key.identity.attempt === String(token)) {
343
+ return refused('identity-collision', { task, token });
344
+ }
345
+
346
+ // Step 10: a git work tree of its own, never the caller's (DEC-042).
347
+ const top = findWorkTreeTop(execWorktree).top;
348
+ if (!top) return refused('exec-worktree-unverifiable', { task, token, execWorktree });
349
+ if (real(top) === real(key.top)) return refused('exec-worktree-shared', { task, token, execWorktree });
350
+
351
+ // Step 11: the harness is eligible on effective facts.
352
+ const evaluated = evaluate({ reg, worker, harness, needs, coverage, env, cwd, root, nowMs, platform, edit });
353
+ if (!evaluated.eligible) return refused('harness-ineligible', { task, token, harness, gaps: evaluated.gaps });
354
+
355
+ // Step 12: insert inside the run store's lock; one run per (task, token, childOf, request).
356
+ const runId = newRunId();
357
+ const { identity } = launchEnv({ env, runKey: active.runKey, token, execWorktree, runId, runScoped: Boolean(childOf) });
358
+ const record = {
359
+ runId, task, token, harness, phase: 'starting', execWorktree, createdAt: clock().toISOString(), timeoutSeconds,
360
+ cliVersion: evaluated.version,
361
+ caller: {
362
+ cwd: path.resolve(cwd), runKey: key.runKey, run: key.identity.run, lineage: key.identity.lineage,
363
+ attempt: key.identity.attempt || null, role: key.identity.role, lineageKind: key.identity.lineageKind,
364
+ },
365
+ edit: editRecord, childOf, request: childOf ? o.request : null,
366
+ ...launchRecord(worker, identity, prompt.bytes.toString('utf8'), edit, binding),
367
+ ...(binding ? { binding: { model: binding.model, flag: binding.flag } } : {}),
368
+ descriptor: worker,
369
+ prompt: { path: prompt.path, sha256: prompt.sha256, bytes: prompt.bytes.length },
370
+ needs: evaluated.requirements.map((item ) => ({ requirement: item.requirement, at: item.at ?? null })),
371
+ };
372
+ const inserted = insertRun(file, record);
373
+ if (!inserted.applied) return refused('run-exists', { task, token, runId: inserted.runId, phase: inserted.phase });
374
+
375
+ // The supervisor, detached, with the caller's environment and cwd (IC-014, DEC-022, DEC-038).
376
+ const child = spawn(process.execPath, [...allowedExecArgv((process ).execArgv), SUPERVISOR_FILE, file, runId], {
377
+ detached: true, stdio: 'ignore', cwd, env,
378
+ });
379
+ // A spawn error leaves the run `starting` with no live supervisor, which status reads as lost.
380
+ child.on('error', () => {});
381
+ child.unref();
382
+ if (Number.isInteger(child.pid)) {
383
+ const supervisor = { pid: child.pid, pgid: child.pid, start: platform === 'win32' ? null : processTree.procStart(child.pid) };
384
+ transition(file, runId, ['starting', 'running', 'cancelling'], (run ) => { run.supervisor = supervisor; });
385
+ }
386
+
387
+ // Poll up to 10 s for the record to leave `starting`.
388
+ const startUntil = nowMs() + START_WAIT_MS;
389
+ let run = readRuns(file)[runId];
390
+ while (run && run.phase === 'starting' && nowMs() < startUntil) {
391
+ await sleep(START_POLL_MS);
392
+ run = readRuns(file)[runId];
393
+ }
394
+ const started = { disposition: 'started', runId, phase: run?.phase ?? null, task, token, harness, execWorktree, deadline: run?.deadline ?? null };
395
+ if (!o.wait) return started;
396
+
397
+ // --wait: poll until the run has an end, bounded by the deadline plus 10 s.
398
+ const deadlineMs = Date.parse(run?.deadline) || (Date.parse(record.createdAt) + timeoutSeconds * 1000);
399
+ const waitUntil = deadlineMs + WAIT_GRACE_MS;
400
+ while (run && !run.end && nowMs() < waitUntil) {
401
+ await sleep(WAIT_POLL_MS);
402
+ run = readRuns(file)[runId];
403
+ }
404
+ return { ...statusView(run, ledgerOf(root, slug), { platform, nowMs: nowMs() }), disposition: 'started' };
405
+ }
406
+
407
+ /** 082 IC-014 / IC-015's flag rules for run: exit 2 `usage` on a misuse. */
408
+ function runFlagRules({ edit, childOf, request, execWorktree } ) {
409
+ if (given(childOf)) {
410
+ if (!RUN_ID.test(String(childOf))) throw usage(`--child-of expects 'w-' plus 12 lowercase hex characters, got '${childOf}'`);
411
+ if (given(edit)) throw usage('--edit does not apply with --child-of: a child run is read-only');
412
+ if (!given(request)) throw usage('--child-of needs --request <d-id>.c<n>');
413
+ if (!CHILD_NAME.test(String(request))) throw usage(`--request expects '<d-id>.c<n>', got '${request}'`);
414
+ if (!given(execWorktree)) throw usage('--child-of needs --exec-worktree');
415
+ } else {
416
+ if (given(request)) throw usage('--request applies only with --child-of');
417
+ if (given(execWorktree)) throw usage('--exec-worktree applies only with --child-of');
418
+ }
419
+ }
420
+
421
+ /**
422
+ * IC-019 and IC-017's view: the reported state from the record and liveness, and `verified`,
423
+ * `attemptActive`, `resultOffered` and `withheld` computed now from the record and the ledger.
424
+ * Writes nothing.
425
+ */
426
+ function statusView(run , ledger , { platform, nowMs } ) {
427
+ const isAlive = (proc ) => (proc ? processTree.alive(proc, { platform }) : false);
428
+ let state ;
429
+ let reason = null;
430
+ const extra = {};
431
+ if (run.phase === 'ended') {
432
+ state = run.end?.state ?? 'unknown';
433
+ } else if (run.phase === 'starting') {
434
+ const sup = isAlive(run.supervisor);
435
+ if (sup === true) state = 'starting';
436
+ else [state, reason] = ['unknown', sup === false ? 'supervisor-lost' : 'liveness-unknown'];
437
+ } else {
438
+ const sup = isAlive(run.supervisor);
439
+ const wrk = isAlive(run.worker);
440
+ if (sup === true || wrk === true) {
441
+ state = 'running';
442
+ const deadlineMs = Date.parse(run.deadline);
443
+ extra.supervisorLost = sup !== true;
444
+ extra.overdue = Number.isFinite(deadlineMs) && nowMs > deadlineMs;
445
+ extra.cancel = run.phase === 'cancelling' ? run.cancel ?? null : null;
446
+ } else {
447
+ [state, reason] = ['unknown', sup === false && wrk === false ? 'process-lost' : 'liveness-unknown'];
448
+ }
449
+ }
450
+ const attempt = attemptFor(ledger, run.task, run.token);
451
+ const attemptActive = attempt?.state === 'active';
452
+ const verified = attempt && !attemptActive ? { state: attempt.state, result: attempt.result ?? null, evidenceId: attempt.evidenceId ?? null } : null;
453
+ const { resultOffered, withheld } = resultOfRun(run);
454
+ return {
455
+ disposition: 'status',
456
+ runId: run.runId, task: run.task, token: run.token, harness: run.harness ?? null, identity: run.identity ?? null,
457
+ state, reason, ...extra,
458
+ startedAt: run.startedAt ?? null, endedAt: run.end?.at ?? null, deadline: run.deadline ?? null,
459
+ process: run.process ?? null, report: run.report ?? null, readOnly: run.readOnly ?? null,
460
+ edit: run.edit ?? null, isolation: run.isolation ?? null, childOf: run.childOf ?? null, request: run.request ?? null,
461
+ verified, attemptActive, resultOffered, withheld,
462
+ cleanup: run.cleanup ?? null, supervisorCleanup: run.supervisorCleanup ?? null, attemptCancel: run.attemptCancel ?? null,
463
+ output: run.output ?? null, launchObserved: run.launchObserved ?? null,
464
+ };
465
+ }
466
+
467
+ /**
468
+ * `{ run, view }` for run `runId` in the run store `file`, read so a run that ends between the
469
+ * record read and the liveness probe is seen ended, never `unknown`. The supervisor writes the end
470
+ * before it exits, so a record read before a probe that finds every process gone may predate that
471
+ * end; it is read again after the probe, and only a record still not ended then is `unknown`.
472
+ * Writes nothing.
473
+ */
474
+ function settledStatus(file , runId , ledger , opts , read = readRuns) {
475
+ let run = read(file)[runId] ?? null;
476
+ let view = run ? statusView(run, ledger, opts) : null;
477
+ if (run && run.phase !== 'ended' && view && view.state === 'unknown') {
478
+ run = read(file)[runId] ?? null;
479
+ view = run ? statusView(run, ledger, opts) : null;
480
+ }
481
+ return { run, view };
482
+ }
483
+
484
+ /** The run named by `--worker-run`, or the top-level run on `(task, token)` (`childOf` null, 082
485
+ * IC-015, so a granter is named and never one of its children), or null. */
486
+ function findRun(file , { workerRun, task, token } ) {
487
+ const runs = readRuns(file);
488
+ if (workerRun) return runs[workerRun] ?? null;
489
+ return (Object.values(runs) ).find((run) => run.task === task && run.token === token && (run.childOf ?? null) === null) ?? null;
490
+ }
491
+
492
+ /** `--worker-run`, or `--task` with `--token`, exactly one of the two. */
493
+ function runSelector(workerRun , task , token , action ) {
494
+ if (given(workerRun)) {
495
+ if (given(task) || given(token)) throw usage(`--action ${action} takes --worker-run or --task with --token, not both`);
496
+ if (!RUN_ID.test(String(workerRun))) throw usage(`--worker-run expects 'w-' plus 12 lowercase hex characters, got '${workerRun}'`);
497
+ return { workerRun: String(workerRun) };
498
+ }
499
+ if (!given(task) || !given(token)) throw usage(`--action ${action} needs --worker-run, or --task with --token`);
500
+ return { task: String(task), token: Number(token) };
501
+ }
502
+
503
+ /** One summary line per harness or run, never output file content (IC-025). */
504
+ function summaryLines(body ) {
505
+ const list = body.harnesses || body.candidates;
506
+ if (Array.isArray(list)) {
507
+ const head = body.disposition === 'selected' ? [`selected ${body.harness}`] : (body.disposition === 'none-eligible' ? ['none-eligible'] : []);
508
+ return [...head, ...list.map((item ) => {
509
+ if (body.disposition === 'discovered') return `${item.harness} ${item.present ? `present ${item.path} ${item.version ?? ''}`.trim() : `absent (${item.reason})`}`;
510
+ if (body.disposition === 'probed') return `${item.harness} probed at ${item.recordedAt}`;
511
+ return `${item.harness} ${item.eligible ? 'eligible' : `ineligible: ${(item.gaps || []).map((g ) => g.requirement).join(', ')}`}`;
512
+ })];
513
+ }
514
+ const parts = [body.disposition];
515
+ if (body.harness && body.disposition === 'described') parts.push(body.harness, body.eligible ? 'eligible' : 'ineligible');
516
+ if (body.runId) parts.push(body.runId);
517
+ if (body.path) parts.push(body.path);
518
+ if (Array.isArray(body.establishes) && body.establishes.length) parts.push(`establishes ${body.establishes.join(', ')}`);
519
+ if (body.task) parts.push(`task ${body.task}`);
520
+ if (body.token !== undefined) parts.push(`token ${body.token}`);
521
+ if (body.state) parts.push(body.state);
522
+ if (body.endState) parts.push(body.endState);
523
+ if (body.run === null) parts.push('no run');
524
+ if (body.reason) parts.push(`(${body.reason})`);
525
+ return [parts.join(' ')];
526
+ }
527
+
528
+ /**
529
+ * Runs one `worker` action and returns `{ exit, body }` without printing. Same options as
530
+ * `handleWorkerCommand`.
531
+ */
532
+ async function executeWorkerCommand({
533
+ action, slug = null, harness, need, modelCheck, budgetTokens, task, token, promptFile, timeoutSeconds, wait = false,
534
+ workerRun, checks, out, by, reason, edit = false, childOf, request, execWorktree,
535
+ env = process.env, cwd = process.cwd(), resolveSlug = null, platform = process.platform, now = () => new Date(),
536
+ descriptors, registry, coverage, sleep = defaultSleep, binding = null,
537
+ } = {}) {
538
+ try {
539
+ if (!given(action)) throw usage(`--action is required; valid: ${ACTIONS.join(' | ')}`);
540
+ if (!ACTIONS.includes(action)) throw usage(`unknown action '${action}'; valid: ${ACTIONS.join(' | ')}`);
541
+ refuseMisplacedFlags(action, { slug, harness, need, modelCheck, budgetTokens, task, token, promptFile, timeoutSeconds, wait, workerRun, checks, out, by, reason, edit, childOf, request, execWorktree });
542
+ if (given(task) && !TASK_ID.test(String(task))) throw usage(`--task expects a plan task id such as A.1, got '${task}'`);
543
+ if (given(token)) digits(token, '--token', 1);
544
+ if (given(budgetTokens) && !given(modelCheck)) throw usage('--budget-tokens applies only with --model-check');
545
+ const clock = typeof now === 'function' ? () => { const at = now(); return at instanceof Date ? at : new Date(at); } : () => new Date(now);
546
+ const nowMs = () => clock().getTime();
547
+ const needs = needList(need);
548
+ const reg = registryOf(registry);
549
+ const cov = coverage === undefined ? reg.coverage : coverage;
550
+ if (needs.length) readiness.validateNeeds(needs, cov);
551
+ // IC-005: one lifecycle project root per invocation; probe records are written and read there.
552
+ const root = lifecycleProjectRoot(cwd);
553
+ const common = { descriptors, registry: reg, env, cwd, now: nowMs, platform };
554
+ const atRoot = { ...common, root };
555
+
556
+ let body ;
557
+ switch (action) {
558
+ case 'discover': body = readiness.discover(common); break;
559
+ case 'probe': body = await readiness.probe({ ...atRoot, coverage: cov, needs, harness: given(harness) ? String(harness) : undefined, modelCheck: given(modelCheck) ? String(modelCheck) : undefined, budgetTokens: digits(budgetTokens, '--budget-tokens', 1) }); break;
560
+ case 'describe': {
561
+ if (!given(harness)) throw usage('--action describe needs --harness');
562
+ let primary ;
563
+ try {
564
+ const { resolvePrimary } = require('../context/primary.js');
565
+ primary = resolvePrimary({ env, projectRoot: cwd, registry: reg });
566
+ } catch (error ) {
567
+ primary = { state: 'unavailable', message: error?.message || String(error) };
568
+ }
569
+ body = readiness.describe({
570
+ ...atRoot, coverage: cov, harness: String(harness), needs, edit: given(edit),
571
+ active: activeWorkers({ registry: reg, env, projectRoot: root }),
572
+ primary,
573
+ bindings: readBindings({ env, projectRoot: root }),
574
+ });
575
+ break;
576
+ }
577
+ case 'select': body = readiness.select({ ...atRoot, coverage: cov, harness: given(harness) ? String(harness) : undefined, needs, edit: given(edit), active: activeWorkers({ registry: reg, env, projectRoot: root }) }); break;
578
+ case 'run': {
579
+ const feature = featureOf(slug, resolveSlug, cwd);
580
+ if (!given(harness)) throw usage('--action run needs --harness');
581
+ if (!given(task) || !given(token)) throw usage('--action run needs --task and --token');
582
+ if (!given(promptFile)) throw usage('--action run needs --prompt-file');
583
+ runFlagRules({ edit, childOf, request, execWorktree });
584
+ const worker = workerBlock(reg, descriptors, String(harness));
585
+ const gate = workerGate(activeWorkers({ registry: reg, env, projectRoot: root }), String(harness));
586
+ if (gate) {
587
+ body = refused(gate.reason, { harness: gate.harness, enabled: gate.enabled, origin: gate.origin });
588
+ break;
589
+ }
590
+ const seconds = digits(timeoutSeconds, '--timeout-seconds', 1, TIMEOUT_MAX_SECONDS) ?? TIMEOUT_DEFAULT_SECONDS;
591
+ const prompt = readPrompt(promptFile, cwd, worker);
592
+ body = await runAction({
593
+ env, cwd, root, platform, clock, sleep, slug: feature, harness: String(harness), task: String(task), token: Number(token),
594
+ needs, timeoutSeconds: seconds, prompt, worker, reg, coverage: cov, wait: Boolean(wait),
595
+ binding,
596
+ edit: given(edit), childOf: given(childOf) ? String(childOf) : null, request: given(request) ? String(request) : null,
597
+ execWorktree: given(execWorktree) ? String(execWorktree) : null,
598
+ });
599
+ break;
600
+ }
601
+ case 'status': {
602
+ const feature = featureOf(slug, resolveSlug, cwd);
603
+ const selector = runSelector(workerRun, task, token, 'status');
604
+ const run = findRun(storeFileFor(root, feature), selector);
605
+ if (run) body = statusView(run, ledgerOf(root, feature), { platform, nowMs: nowMs() });
606
+ else if (selector.workerRun) body = refused('unknown-run', { runId: selector.workerRun });
607
+ else body = { disposition: 'status', run: null, task: selector.task, token: selector.token };
608
+ break;
609
+ }
610
+ case 'evidence': {
611
+ if (!given(harness)) throw usage('--action evidence needs --harness');
612
+ if (!given(out)) throw usage('--action evidence needs --out');
613
+ const hasChecks = given(checks);
614
+ const hasWorkerRun = given(workerRun);
615
+ if (hasChecks === hasWorkerRun) throw usage('--action evidence needs exactly one of --checks or --worker-run');
616
+ const worker = workerBlock(reg, descriptors, String(harness));
617
+ const outFile = path.resolve(cwd, String(out));
618
+ if (hasChecks) {
619
+ body = evidence.recordCliChecks({
620
+ checksFile: path.resolve(cwd, String(checks)), harness: String(harness), worker, out: outFile,
621
+ cwd: root, env, now: clock, platform,
622
+ });
623
+ } else {
624
+ if (!RUN_ID.test(String(workerRun))) throw usage(`--worker-run expects 'w-' plus 12 lowercase hex characters, got '${workerRun}'`);
625
+ const feature = featureOf(slug, resolveSlug, cwd);
626
+ const run = readRuns(storeFileFor(root, feature))[String(workerRun)] ?? null;
627
+ body = evidence.recordLiveRun({ run, harness: String(harness), worker, out: outFile, cwd: root, now: clock });
628
+ }
629
+ break;
630
+ }
631
+ default: {
632
+ const feature = featureOf(slug, resolveSlug, cwd);
633
+ const selector = runSelector(workerRun, task, token, 'cancel');
634
+ if (!given(by) || !SAFE_NAME.test(String(by))) throw usage('--by is required: the name of the agent or person cancelling the run');
635
+ if (typeof reason !== 'string' || !reason.trim()) throw usage('--reason is required: why the run is being cancelled');
636
+ const file = storeFileFor(root, feature);
637
+ const run = findRun(file, selector);
638
+ if (!run) {
639
+ // IC-021 step 1 order: the caller's identity before the run's existence.
640
+ const key = runKeyFor(env, cwd);
641
+ const who = identityRefusal(key.identity);
642
+ body = who ? refused(who.reason, { runKey: key.runKey }) : refused('unknown-run', selector.workerRun ? { runId: selector.workerRun } : { task: selector.task, token: selector.token });
643
+ } else {
644
+ body = await cancelRun({ file, runId: run.runId, by: String(by), reason, source: 'user', env, cwd, platform, now: clock });
645
+ }
646
+ }
647
+ }
648
+ return { exit: EXIT_ZERO.includes(body.disposition) ? 0 : 1, body };
649
+ } catch (error ) {
650
+ if (error instanceof evidence.WorkerEvidenceError) {
651
+ if (error.code === 'refused') return { exit: 1, body: { disposition: 'refused', reason: error.reason } };
652
+ if (error.code === 'usage') return { exit: 2, body: { ok: false, error: 'usage', reason: error.reason, message: error.message } };
653
+ }
654
+ const code = typeof error?.code === 'string' ? error.code : null;
655
+ if (code && ERROR_CODES.includes(code)) return { exit: 2, body: { ok: false, error: code, message: error.message } };
656
+ // launch.ts's caller errors (run-key-malformed, prompt-too-large, recipe-incomplete) are usage.
657
+ if (error?.name === 'LaunchUsageError') return { exit: 2, body: { ok: false, error: 'usage', reason: code, message: error.message } };
658
+ throw error;
659
+ }
660
+ }
661
+
662
+ /**
663
+ * `doflow-run worker --action <action> ...` (IC-025). Prints the JSON with `json`, else one summary
664
+ * line per harness or run, sets `process.exitCode` and resolves the exit code: 0 for the applied and
665
+ * read dispositions (including `recorded`), 1 for `refused` and `none-eligible`, 2 for `usage`, `store-unreadable` and
666
+ * `registry-invalid`. Callers branch on the JSON, never on the exit code alone.
667
+ */
668
+ async function handleWorkerCommand(options = {}) {
669
+ const { finishRuntime } = require('../cli-result.js');
670
+ const { exit, body } = await executeWorkerCommand(options);
671
+ if (options.json) console.log(JSON.stringify(body, null, 2));
672
+ else if (exit === 2) console.error(`doflow worker: ${body.message}`);
673
+ else for (const line of summaryLines(body)) (exit === 0 ? console.log : console.error)(line);
674
+ return finishRuntime(exit);
675
+ }
676
+
677
+ module.exports = { handleWorkerCommand, executeWorkerCommand, statusView, settledStatus, findRun, allowedExecArgv, ACTIONS, FLAG_ACTIONS, WorkerCommandError };