@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
@@ -10,6 +10,7 @@ const { finishRuntime } = require('./cli-result.js');
10
10
  const { REPO_ROOT } = require('../helper/repo-root.js');
11
11
  const { loadAdviceRegistry } = require('./advice/registry.js');
12
12
  const { createAdviceClient } = require('./advice/client.js');
13
+ const { resolveShadowConfig } = require('./advice/shadow-config.js');
13
14
  const { materializeKind } = require('./advice/kinds.js');
14
15
  const { appendOutcome, findPick } = require('./advice/record.js');
15
16
  const { strengthWords } = require('./advice/strength.js');
@@ -438,7 +439,7 @@ class TaskClassifier {
438
439
  * @param {boolean} [options.json=false]
439
440
  * @returns {number|Promise<number>} exit code; advisory calls are asynchronous
440
441
  */
441
- function handleClassifyCommand({ taskClass, rationale, proposedBy, callingSkill, request, requestFile, taskId, projectRoot, json = false } = {}) {
442
+ function handleClassifyCommand({ taskClass, rationale, proposedBy, callingSkill, request, requestFile, taskId, projectRoot, env = process.env, json = false } = {}) {
442
443
  const classifier = new TaskClassifier({ repoRoot: REPO_ROOT });
443
444
  const decision = classifier.classify({ taskClass, rationale, proposedBy, callingSkill });
444
445
  const exitCode = decision.outcome === CLASSIFICATION_OUTCOMES.ACCEPTED
@@ -470,22 +471,57 @@ function handleClassifyCommand({ taskClass, rationale, proposedBy, callingSkill,
470
471
  try {
471
472
  const advice = loadAdviceRegistry({ repoRoot: REPO_ROOT });
472
473
  const storage = loadStorageRegistry({ repoRoot: REPO_ROOT });
473
- const roots = resolveRoots({ registry: storage, projectRoot: path.resolve(projectRoot || process.cwd()) });
474
+ const roots = resolveRoots({ registry: storage, projectRoot: path.resolve(projectRoot || process.cwd()), env });
474
475
  const source = (name ) => JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'core/registry', name), 'utf8'));
475
476
  const sources = { workflows: source('workflows.json') };
476
477
  const built = materializeKind('task-class', { adviceRegistry: advice.ok ? advice.registry : null, sources });
477
478
  const client = createAdviceClient({ adviceRegistry: advice.ok ? advice.registry : null, roots, sources });
478
- const result = advice.ok && built.ok
479
- ? await client.ask({
480
- questionSet: { state, questions: [built.question] }, kind: 'task-class',
481
- staticAnswer: taskClass ?? null, caller: 'classify', taskId: taskId ?? null, record: true,
482
- })
483
- : { status: 'unavailable', reason: 'config-invalid', answers: [] };
479
+ const resolvedRoot = path.resolve(projectRoot || process.cwd());
480
+ const shadow = resolveShadowConfig('task-class', { projectRoot: resolvedRoot, env, roots });
481
+ let result = { status: 'unavailable', reason: 'config-invalid', answers: [] };
482
+ // The pick an outcome is recorded against: the shown answer's, or a shadow pick the client recorded.
483
+ let recordedPickId = null;
484
+ if (advice.ok && built.ok && !shadow.kindSet) {
485
+ // advice.kinds.task-class unset: the client asks and records the pick itself, as before the dispatcher.
486
+ // A chosen rollout profile caps it: off asks nothing; shadow records the pick and shows none.
487
+ const { profileAdvisorCap, unsetKindPath } = require('./advice/mode.js');
488
+ const route = unsetKindPath(profileAdvisorCap({ projectRoot: resolvedRoot, env }));
489
+ if (route === 'off') result = { status: 'off', reason: null, answers: [] };
490
+ else {
491
+ result = await client.ask({
492
+ questionSet: { state, questions: [built.question] }, kind: 'task-class',
493
+ staticAnswer: taskClass ?? null, caller: 'classify', taskId: taskId ?? null, record: true,
494
+ });
495
+ if (route === 'shadow' && result?.status === 'answered') {
496
+ recordedPickId = result.answers?.[0]?.pickId ?? null;
497
+ result = { status: 'shadow', reason: null, answers: [] };
498
+ }
499
+ }
500
+ } else if (advice.ok && built.ok) {
501
+ const { interceptDecisionPoint } = require('./advice/shadow-dispatcher.js');
502
+ const intercepted = await interceptDecisionPoint({
503
+ kind: 'task-class', caller: 'classify', state, staticPick: taskClass || 'unknown',
504
+ staticAnswer: taskClass ?? null,
505
+ questionSet: { state, questions: [built.question] },
506
+ adviceRegistry: advice.registry, taskId: taskId ?? null, projectRoot: resolvedRoot, env,
507
+ }, { adviceClient: client, roots, shadowConfig: shadow });
508
+ // Only act shows a pick, and it is the decision after the raise-only rule. The dispatcher records the
509
+ // pick (IC-003); its pickId is the one findPick resolves below.
510
+ if (intercepted.mode === 'act' && intercepted.advisorPick) {
511
+ result = { status: 'answered', model: intercepted.advisorAnswer.model, answers: [{
512
+ ...intercepted.advisorAnswer, pick: intercepted.effectiveDecision, pickId: intercepted.pickId,
513
+ agrees: intercepted.effectiveDecision === taskClass,
514
+ }] };
515
+ } else if (intercepted.mode === 'shadow') result = { status: 'shadow', reason: intercepted.warning || null, answers: [] };
516
+ else if (intercepted.mode === 'off') result = { status: 'off', reason: null, answers: [] };
517
+ else result = { status: 'unavailable', reason: intercepted.warning || null, answers: [] };
518
+ }
484
519
  opinion = secondOpinion(result, taskClass);
485
- if (decision.outcome === CLASSIFICATION_OUTCOMES.ACCEPTED && opinion.status === 'answered' && opinion.pickId) {
486
- const found = findPick({ pickId: opinion.pickId, roots });
520
+ const outcomePickId = opinion.status === 'answered' ? opinion.pickId : recordedPickId;
521
+ if (decision.outcome === CLASSIFICATION_OUTCOMES.ACCEPTED && outcomePickId) {
522
+ const found = findPick({ pickId: outcomePickId, roots });
487
523
  if (found) appendOutcome({
488
- pickId: opinion.pickId, actual: decision.taskClass, source: 'classify-verdict', roots, store: found.store,
524
+ pickId: outcomePickId, actual: decision.taskClass, source: 'classify-verdict', roots, store: found.store,
489
525
  });
490
526
  }
491
527
  } catch {
@@ -0,0 +1,373 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ // Task graph (feature 080, design C2): the plan's tasks as a dependency graph, validated before any
6
+ // ledger rule reads it.
7
+ //
8
+ // Pure over `parsePlan` output: no file is read and nothing is written here. `buildGraph` collects
9
+ // every IC-003 problem before it returns, so one refusal names everything wrong with a plan rather
10
+ // than the first thing. The cycle search covers the explicit `depends-on:` task edges and the
11
+ // implicit phase edges together (a task with no task dependency waits on every task of an earlier
12
+ // phase, IC-005 row 9): validating explicit edges alone accepts a plan whose phase order and
13
+ // declared dependencies wait on each other forever (design debate A-M1).
14
+
15
+ const crypto = require('node:crypto');
16
+ const { parsePlan } = require('./scaffold/artifacts.js');
17
+
18
+ /** IC-008 / DEC-002: the attempt budget of a task that declares no `budget:`. */
19
+ const DEFAULT_TASK_ATTEMPTS = 20;
20
+
21
+ /** IC-001 acceptance grammar: a requirement, interface contract, story or scenario id. */
22
+ const ACCEPTANCE = [/^(FR|NFR|IC)-\d{3}$/, /^US\d+$/, /^S-[a-z0-9]+(-[a-z0-9]+)*$/];
23
+
24
+ /** DEC-008: only the `**Task graph:** v1` header makes a plan a graph plan. Accepts a parsed plan
25
+ * or a graph, both of which carry `graphHeader`. */
26
+ function isGraphPlan(planOrGraph ) {
27
+ return Boolean(planOrGraph) && planOrGraph.graphHeader === 'v1';
28
+ }
29
+
30
+ /** IC-016: a `files:` entry compared after trimming and dropping a leading `./`. */
31
+ function normalizeFile(file ) {
32
+ return String(file).trim().replace(/^(\.\/)+/, '');
33
+ }
34
+
35
+ /**
36
+ * Explicit and implicit edges out of each task, in plan order. An edge `from -> to` means `from`
37
+ * waits on `to`. Explicit: each known `taskDeps` value. Implicit (`phase: true`): a task with no
38
+ * `taskDeps` waits on every task of an earlier phase. Unknown dependencies have no edge; they are
39
+ * reported as `unknown-dependency` instead.
40
+ */
41
+ function outEdges(tasks , byId ) {
42
+ const edges = new Map ();
43
+ for (const task of tasks) {
44
+ if (edges.has(task.id)) continue; // a duplicate id: the first occurrence stands for it
45
+ const out = [];
46
+ if (task.taskDeps.length) {
47
+ for (const dep of [...new Set(task.taskDeps )]) if (byId.has(dep)) out.push({ to: dep, phase: false });
48
+ } else {
49
+ for (const other of tasks) {
50
+ if (other.phaseIndex < task.phaseIndex && !out.some((e) => e.to === other.id)) out.push({ to: other.id, phase: true });
51
+ }
52
+ }
53
+ edges.set(task.id, out);
54
+ }
55
+ return edges;
56
+ }
57
+
58
+ /**
59
+ * Iterative three-colour depth-first search over `edges`, roots and successors in plan order, so
60
+ * the cycles reported and their order are deterministic. Returns every distinct cycle closed by a
61
+ * back edge, and a topological order (dependencies first) that is valid when no cycle exists.
62
+ */
63
+ function searchCycles(ids , edges ) {
64
+ const WHITE = 0, GREY = 1, BLACK = 2;
65
+ const colour = new Map (ids.map((id) => [id, WHITE]));
66
+ const order = [];
67
+ const cycles = [];
68
+ const seenCycles = new Set ();
69
+
70
+ for (const root of ids) {
71
+ if (colour.get(root) !== WHITE) continue;
72
+ // Each frame: the node, the index of its next edge, and the edge that entered it.
73
+ const stack = [{ id: root, next: 0 }];
74
+ const entered = [null];
75
+ colour.set(root, GREY);
76
+ while (stack.length) {
77
+ const frame = stack[stack.length - 1];
78
+ const out = edges.get(frame.id) || [];
79
+ if (frame.next >= out.length) {
80
+ colour.set(frame.id, BLACK);
81
+ order.push(frame.id);
82
+ stack.pop();
83
+ entered.pop();
84
+ continue;
85
+ }
86
+ const edge = out[frame.next++];
87
+ const state = colour.get(edge.to);
88
+ if (state === WHITE) {
89
+ colour.set(edge.to, GREY);
90
+ stack.push({ id: edge.to, next: 0 });
91
+ entered.push(edge);
92
+ } else if (state === GREY) {
93
+ const start = stack.findIndex((f) => f.id === edge.to);
94
+ const cycleIds = stack.slice(start).map((f) => f.id);
95
+ const via = [];
96
+ for (let i = start + 1; i < stack.length; i += 1) {
97
+ via.push(`${stack[i - 1].id}->${stack[i].id}${entered[i] .phase ? ':phase' : ''}`);
98
+ }
99
+ via.push(`${frame.id}->${edge.to}${edge.phase ? ':phase' : ''}`);
100
+ const key = via.join('|');
101
+ if (!seenCycles.has(key)) {
102
+ seenCycles.add(key);
103
+ cycles.push({ kind: 'cycle', ids: cycleIds, via });
104
+ }
105
+ }
106
+ }
107
+ }
108
+ return { cycles, order };
109
+ }
110
+
111
+ /**
112
+ * The one owner comparison key (plan-check's review-owner warning and the claim's owner check):
113
+ * the owner without a trailing parenthetical note, trimmed and lowercased. The owner of record stays raw.
114
+ */
115
+ function normalizeOwner(owner ) {
116
+ return String(owner ?? '').replace(/\s*\([^()]*\)\s*$/, '').trim().toLowerCase();
117
+ }
118
+
119
+ /**
120
+ * IC-003: `parsePlan` output → `{ ok: true, graph, warnings }` or `{ ok: false, problems }`, with
121
+ * every problem collected first. The graph keeps the parsed tasks in plan order with `phaseIndex`
122
+ * (phase order is first appearance), plus `order`, a topological order with dependencies first.
123
+ */
124
+ function buildGraph(parsed ) {
125
+ const graphHeader = parsed.graphHeader === undefined ? null : parsed.graphHeader;
126
+ const graphPlan = graphHeader === 'v1';
127
+ const phases = [...new Set (parsed.tasks.map((t ) => t.phase))];
128
+ const tasks = parsed.tasks.map((t ) => ({
129
+ ...t,
130
+ phaseIndex: phases.indexOf(t.phase),
131
+ taskDeps: t.taskDeps || [],
132
+ nearMissDeps: t.nearMissDeps || [],
133
+ acceptance: t.acceptance || [],
134
+ budget: t.budget === undefined ? null : t.budget,
135
+ parallel: Boolean(t.parallel),
136
+ }));
137
+ const problems = [];
138
+ const warnings = [];
139
+
140
+ if (graphHeader && typeof graphHeader === 'object') problems.push({ kind: 'bad-header', ids: [], value: graphHeader.invalid });
141
+
142
+ const byId = new Map ();
143
+ const reported = new Set ();
144
+ for (const task of tasks) {
145
+ if (!byId.has(task.id)) { byId.set(task.id, task); continue; }
146
+ if (!reported.has(task.id)) { reported.add(task.id); problems.push({ kind: 'duplicate-id', ids: [task.id] }); }
147
+ }
148
+
149
+ for (const task of tasks) {
150
+ for (const dep of task.taskDeps) {
151
+ if (!byId.has(dep)) problems.push({ kind: 'unknown-dependency', ids: [task.id, dep] });
152
+ }
153
+ for (const value of task.nearMissDeps) {
154
+ if (graphPlan) problems.push({ kind: 'bad-dependency', ids: [task.id], value });
155
+ else warnings.push({ kind: 'near-miss-dependency', ids: [task.id], value });
156
+ }
157
+ for (const value of task.acceptance) {
158
+ if (!ACCEPTANCE.some((pattern) => pattern.test(value))) problems.push({ kind: 'bad-acceptance', ids: [task.id], value });
159
+ }
160
+ // IC-010: quality-guardian is the review hold, so a frontier-tier task there is mis-owned.
161
+ // A trailing model note such as 'quality-guardian (Opus)' is stripped (`normalizeOwner`); a claim compares owners the same way.
162
+ if (typeof task.owner === 'string' && normalizeOwner(task.owner) === 'quality-guardian' && task.tier === 'frontier') {
163
+ warnings.push({ kind: 'review-owner', ids: [task.id], value: 'quality-guardian' });
164
+ }
165
+ if (task.budget && typeof task.budget === 'object' && 'invalid' in task.budget) {
166
+ problems.push({ kind: 'bad-budget', ids: [task.id], value: task.budget.invalid });
167
+ }
168
+ }
169
+
170
+ const ids = [...byId.keys()];
171
+ const { cycles, order } = searchCycles(ids, outEdges(tasks, byId));
172
+ problems.push(...cycles);
173
+
174
+ if (problems.length) return { ok: false, problems, warnings };
175
+ return { ok: true, graph: { graphHeader, graphPlan, phases, tasks, order }, warnings };
176
+ }
177
+
178
+ /** IC-002: the canonical plan judgment, including plans with no task lines. */
179
+ function planVerdict(text )
180
+
181
+
182
+
183
+
184
+ {
185
+ const parsed = parsePlan(text);
186
+ const built = buildGraph(parsed);
187
+ const problems = [...(built.problems || [])];
188
+ if (parsed.tasks.length === 0) problems.push({ kind: 'no-tasks', ids: [] });
189
+ const warnings = (built.warnings || []).map((warning ) => {
190
+ const detail = warning.via
191
+ ? ` via ${warning.via.join(', ')}`
192
+ : warning.value !== undefined ? ` '${warning.value}'` : '';
193
+ return `${warning.kind}${warning.ids.length ? ` ${warning.ids.join(' ')}` : ''}${detail}`;
194
+ });
195
+ return { ok: problems.length === 0, tasks: parsed.tasks.length, problems, warnings };
196
+ }
197
+
198
+ /** IC-004: one tuple per task in plan order — the structure and declared files, never the
199
+ * description or the checkbox. */
200
+ function graphTuples(graph ) {
201
+ return graph.tasks.map((t ) => [t.id, t.phase, t.owner, t.taskDeps, t.parallel, t.acceptance, t.budget, t.files]);
202
+ }
203
+
204
+ /** IC-004: `sha256:` over the tuples' canonical JSON (arrays and one-key objects only, so
205
+ * `JSON.stringify` is already canonical). */
206
+ function graphDigest(graph ) {
207
+ return `sha256:${crypto.createHash('sha256').update(JSON.stringify(graphTuples(graph))).digest('hex')}`;
208
+ }
209
+
210
+ /** IC-016: every pair of tasks, in plan order, declaring the same `files:` entry after
211
+ * normalisation, with the shared entries in the first task's declaration order. */
212
+ function overlaps(graph ) {
213
+ const declared = graph.tasks.map((t ) => ({ id: t.id, files: [...new Set ((t.files || []).map(normalizeFile))].filter(Boolean) }));
214
+ const pairs = [];
215
+ for (let i = 0; i < declared.length; i += 1) {
216
+ for (let j = i + 1; j < declared.length; j += 1) {
217
+ const other = new Set(declared[j].files);
218
+ const files = declared[i].files.filter((f ) => other.has(f));
219
+ if (files.length) pairs.push({ a: declared[i].id, b: declared[j].id, files });
220
+ }
221
+ }
222
+ return pairs;
223
+ }
224
+
225
+ /** IC-008: the task's attempt limit and where it came from. An invalid `budget:` is refused by
226
+ * `buildGraph`, so here it can only mean the default. */
227
+ function taskAttemptLimit(task ) {
228
+ const attempts = task && task.budget && Number.isInteger(task.budget.attempts) ? task.budget.attempts : null;
229
+ return attempts === null ? { limit: DEFAULT_TASK_ATTEMPTS, source: 'default' } : { limit: attempts, source: 'plan' };
230
+ }
231
+
232
+ // ── derivation (IC-005, IC-008 `used`, IC-024 done-stale) ─────────────────────────────────────
233
+
234
+ /** IC-005 `state` → the pre-080 `derived` vocabulary, which legacy flows keep reading. */
235
+ const DERIVED = {
236
+ SUCCEEDED: 'done', RUNNING: 'running', BLOCKED: 'blocked', CANCELLED: 'cancelled',
237
+ FAILED: 'failed', PLANNED: 'pending', READY: 'ready',
238
+ };
239
+
240
+ /** A dependency in one of these states holds its dependents (IC-005 rows 7 and 9, `held`). */
241
+ const HOLDING = new Set(['BLOCKED', 'FAILED', 'CANCELLED']);
242
+
243
+ /** IC-005 "latest ended": the highest-token attempt that is `finished`, `cancelled` or
244
+ * `superseded`. `released` is skipped, as the pre-080 `latestFinished` skips it (design debate
245
+ * A-M2): counting it would turn a legacy BLOCKED-then-released task from `blocked` into `ready`. */
246
+ function latestEnded(attempts ) {
247
+ let latest = null;
248
+ for (const a of attempts) {
249
+ if (a.state !== 'finished' && a.state !== 'cancelled' && a.state !== 'superseded') continue;
250
+ if (!latest || a.token > latest.token) latest = a;
251
+ }
252
+ return latest;
253
+ }
254
+
255
+ /** DEC-016 done-attempt-selection (IC-005 Terms): the done attempt is the highest-token `finished`
256
+ * attempt with result COMPLETED. A store holding one (every legacy and `8ce5ad7` store) is
257
+ * unchanged; a redo of a stale-done task wins over the stale attempt. */
258
+ function doneOf(attempts ) {
259
+ let done = null;
260
+ for (const a of attempts) {
261
+ if (a.state === 'finished' && a.result === 'COMPLETED' && (!done || a.token > done.token)) done = a;
262
+ }
263
+ return done;
264
+ }
265
+
266
+ /** IC-008: attempts carrying a `runKey`, in any state, plus every `superseded` attempt. */
267
+ function attemptsUsed(attempts ) {
268
+ return attempts.filter((a ) => Boolean(a.runKey) || a.state === 'superseded').length;
269
+ }
270
+
271
+ /** The current line hash of `task`: from `hashes` (a Map or a plain object keyed by task id), else
272
+ * the `hash` a `loadPlan` task carries. */
273
+ function currentHash(task , hashes ) {
274
+ if (hashes instanceof Map) { if (hashes.has(task.id)) return hashes.get(task.id); }
275
+ else if (hashes && typeof hashes === 'object' && Object.prototype.hasOwnProperty.call(hashes, task.id)) return hashes[task.id];
276
+ return task.hash;
277
+ }
278
+
279
+ /**
280
+ * IC-005 rows 7 to 9 for one task, against the states already derived for its direct
281
+ * dependencies: the explicit `taskDeps`, or every task of an earlier phase when there are none.
282
+ * `held` is direct; a dependency that is PLANNED for its own reason is named in `unmet`.
283
+ */
284
+ function readinessOf(task , tasks , states ) {
285
+ if (task.taskDeps.length) {
286
+ const deps = [...new Set (task.taskDeps)];
287
+ const held = deps.filter((id) => HOLDING.has(states.get(id) )).map((id) => ({ id, state: states.get(id) }));
288
+ if (held.length) return { reason: 'dependency-held', held };
289
+ const unmet = deps.filter((id) => states.get(id) !== 'SUCCEEDED');
290
+ if (unmet.length) return { reason: 'unmet-dependency', unmet };
291
+ return { reason: 'ready' };
292
+ }
293
+ const earlier = tasks.filter((o ) => o.phaseIndex < task.phaseIndex);
294
+ const unmet = earlier.filter((o ) => states.get(o.id) !== 'SUCCEEDED').map((o ) => o.id);
295
+ if (!unmet.length) return { reason: 'ready' };
296
+ const held = earlier.filter((o ) => HOLDING.has(states.get(o.id) )).map((o ) => ({ id: o.id, state: states.get(o.id) }));
297
+ return { reason: 'unmet-phase', unmet, held };
298
+ }
299
+
300
+ /**
301
+ * IC-005: one record per task, in plan order, evaluated in `graph.order` (dependencies first) so
302
+ * each task reads its dependencies' final states. Pure: the store is read, never changed, and the
303
+ * cleared cancels (a later `resume` of the cancelling run, IC-015) are computed by the caller.
304
+ *
305
+ * @param graph `buildGraph(...).graph`
306
+ * @param store the task-attempt ledger object (`{ tasks: { <id>: { attempts } } }`)
307
+ * @param options.clearedCancels iterable of `{ taskId, token }` naming cancelled attempts a resume
308
+ * has cleared (IC-005 row 4)
309
+ * @param options.hashes current line hash per task id (Map or object); defaults to each graph
310
+ * task's own `hash`. Read only in a graph plan, for a task with a done attempt (IC-024).
311
+ */
312
+ function deriveTasks(graph , store , { clearedCancels = [], hashes = null } = {}) {
313
+ const graphPlan = isGraphPlan(graph);
314
+ const cleared = new Set ();
315
+ for (const c of clearedCancels || []) cleared.add(`${c.taskId}#${c.token}`);
316
+ const byId = new Map (graph.tasks.map((t ) => [t.id, t]));
317
+ const states = new Map ();
318
+ const records = new Map ();
319
+
320
+ for (const id of graph.order) {
321
+ const task = byId.get(id);
322
+ const entry = store && store.tasks ? store.tasks[id] : null;
323
+ const attempts = entry && Array.isArray(entry.attempts) ? entry.attempts : [];
324
+ const active = attempts.find((a ) => a.state === 'active') || null;
325
+ let done = doneOf(attempts);
326
+ let doneStale = null;
327
+ if (done && graphPlan) {
328
+ const hash = currentHash(task, hashes);
329
+ if (typeof hash !== 'string') throw new TypeError(`deriveTasks: no current line hash for task '${id}'; pass options.hashes or loadPlan tasks`);
330
+ if (hash !== done.taskLineHash) { doneStale = { token: done.token }; done = null; }
331
+ }
332
+ const last = latestEnded(attempts);
333
+ const { limit, source } = taskAttemptLimit(task);
334
+ const budget = { limit, used: attemptsUsed(attempts), source };
335
+
336
+ let state , reason , claimable = false;
337
+ let readiness = null;
338
+ const extra = {};
339
+ if (done) { state = 'SUCCEEDED'; reason = 'completed'; }
340
+ else if (active) { state = 'RUNNING'; reason = 'active-attempt'; }
341
+ else if (budget.used >= budget.limit) { state = 'BLOCKED'; reason = 'budget'; }
342
+ else if (last && last.state === 'cancelled' && !cleared.has(`${id}#${last.token}`)) { state = 'CANCELLED'; reason = 'cancelled'; }
343
+ else {
344
+ readiness = readinessOf(task, graph.tasks, states);
345
+ const ready = readiness.reason === 'ready';
346
+ if (last && last.state === 'finished' && last.result === 'FAILED') { state = 'FAILED'; reason = 'failed'; claimable = ready; }
347
+ else if (last && last.state === 'finished' && last.result === 'BLOCKED') { state = 'BLOCKED'; reason = 'result-blocked'; claimable = ready; }
348
+ else if (!ready) {
349
+ state = 'PLANNED'; reason = readiness.reason;
350
+ if (readiness.held) extra.held = readiness.held;
351
+ if (readiness.unmet) extra.unmet = readiness.unmet;
352
+ } else {
353
+ state = 'READY'; claimable = true;
354
+ reason = doneStale ? 'plan-changed-after-done' : last && last.state === 'superseded' ? 'reopened' : 'ready';
355
+ }
356
+ }
357
+ if (doneStale) extra.doneStale = doneStale;
358
+ states.set(id, state);
359
+ records.set(id, {
360
+ id, phase: task.phase, derived: DERIVED[state], state, reason, claimable, budget, ...extra,
361
+ activeToken: active ? active.token : null,
362
+ doneToken: done ? done.token : null,
363
+ active: active ? { owner: active.owner, mode: active.mode, startedAt: active.startedAt } : null,
364
+ readiness,
365
+ });
366
+ }
367
+ return graph.tasks.map((t ) => records.get(t.id));
368
+ }
369
+
370
+ module.exports = {
371
+ DEFAULT_TASK_ATTEMPTS, buildGraph, planVerdict, isGraphPlan, graphTuples, graphDigest, overlaps, taskAttemptLimit,
372
+ deriveTasks, doneOf, normalizeOwner,
373
+ };