@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,530 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ /**
6
+ * `doflow review --action start` (095b IC-001). The verb checks the verify precondition on HEAD and
7
+ * resolves the review protocol (119 IC-003/IC-005, `resolve-protocol.ts`) in one call. It never
8
+ * prompts, reads no stdin and writes no file and no telemetry line.
9
+ */
10
+
11
+ const fs = require('node:fs');
12
+ const path = require('node:path');
13
+ const { spawnSync } = require('node:child_process');
14
+ const { REPO_ROOT } = require('../../helper/repo-root.js');
15
+ const { verifyRecordReasons, loadPolicy } = require('../roadmap/gates.js');
16
+ const { finishRuntime, captureCaught } = require('../cli-result.js');
17
+ const { invalidProjectCandidate, invalidPresetCandidate } = require('./findings.js');
18
+ const { resolveProtocol, protocolLines, protocolDigest, pathScope, gateMinSeats } = require('./resolve-protocol.js');
19
+
20
+
21
+
22
+ const SLUG_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
23
+ const HEX_HEAD = /^[0-9a-f]{40}$/;
24
+ // Inherited location variables would point the checks at another repository.
25
+ const GIT_LOCATION_KEYS = Object.freeze([
26
+ 'GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_OBJECT_DIRECTORY', 'GIT_COMMON_DIR',
27
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
28
+ ]);
29
+
30
+ function validSlug(value ) {
31
+ return typeof value === 'string' && SLUG_PATTERN.test(value);
32
+ }
33
+
34
+ /** IC-001 step 4: gate-b's verify rule plus the clean-tree rule, sorted and unique. */
35
+ function reviewPrecondition({ records, head, clean, slug } ) {
36
+ const reasons = new Set (verifyRecordReasons(records, head, slug));
37
+ if (!clean) reasons.add('tree-dirty');
38
+ return [...reasons].sort();
39
+ }
40
+
41
+ /** The risk of the last clean line for `slug` on `head` when it is a known level, else the default. */
42
+ function riskOfRecords({ records, head, slug, levels, defaultRisk } ) {
43
+ const matching = records.filter((run ) => run && typeof run === 'object' && run.head === head
44
+ && run.taskId === slug && run.clean === true);
45
+ const risk = matching.length ? matching[matching.length - 1].risk : undefined;
46
+ return typeof risk === 'string' && Object.hasOwn(levels, risk)
47
+ ? { risk, riskSource: 'record' }
48
+ : { risk: defaultRisk, riskSource: 'default' };
49
+ }
50
+
51
+ /** IC-001 step 3, from workflows data only: a review stage directly after a required verification stage. */
52
+ function reviewApplies(classes , taskClass ) {
53
+ const stages = classes?.[taskClass]?.stages;
54
+ if (!Array.isArray(stages)) return false;
55
+ const at = stages.findIndex((stage ) => stage.kind === 'review');
56
+ const before = at > 0 ? stages[at - 1] : null;
57
+ return Boolean(before) && before.kind === 'verification' && before.optional === false;
58
+ }
59
+
60
+ function gitRun(cwd , env , args ) {
61
+ const gitEnv = { ...env };
62
+ for (const key of GIT_LOCATION_KEYS) delete gitEnv[key];
63
+ const res = spawnSync('git', ['-C', cwd, ...args], { cwd, env: gitEnv, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 30000 });
64
+ return { status: res.status, stdout: typeof res.stdout === 'string' ? res.stdout : '' };
65
+ }
66
+
67
+ /** HEAD and the clean flag. A tree git cannot report on is not clean. */
68
+ function readHeadState(cwd , env ) {
69
+ const rev = gitRun(cwd, env, ['rev-parse', '--verify', 'HEAD']);
70
+ const head = rev.status === 0 && HEX_HEAD.test(rev.stdout.trim()) ? rev.stdout.trim() : null;
71
+ const status = gitRun(cwd, env, ['--no-optional-locks', 'status', '--porcelain', '--untracked-files=all']);
72
+ return { head, clean: status.status === 0 && status.stdout.length === 0 };
73
+ }
74
+
75
+ /** The changed paths from the base to the working tree (HEAD on a clean tree); a rename lists both paths. Null when unreadable. */
76
+ function changedPaths(cwd , env , baseSha ) {
77
+ if (!baseSha) return null;
78
+ const diff = gitRun(cwd, env, ['diff', '--no-renames', '--name-only', '-z', baseSha]);
79
+ return diff.status === 0 ? diff.stdout.split('\0').filter(Boolean) : null;
80
+ }
81
+
82
+ /** The largest verify record file the verb reads; a bigger one is `records-unreadable`. */
83
+ const MAX_RECORD_BYTES = 8 * 1024 * 1024;
84
+
85
+ /**
86
+ * A runtime-owned file's text through a bounded, non-blocking read (the pattern of
87
+ * task-scope.ts `readRegularFileText`): `lstat`, then `O_NOFOLLOW|O_NONBLOCK`, `fstat` on the
88
+ * descriptor and a size cap. `missing` for ENOENT; any non-regular target (link, FIFO, device, folder)
89
+ * or oversized file is an error, never a blocking read.
90
+ */
91
+ function readBounded(file , maxBytes ) {
92
+ const flags = fs.constants;
93
+ let fd ;
94
+ try {
95
+ let st = fs.lstatSync(file);
96
+ if (!st.isFile()) return { error: 'not a regular file' };
97
+ fd = fs.openSync(file, flags.O_RDONLY | (flags.O_NOFOLLOW || 0) | (flags.O_NONBLOCK || 0));
98
+ st = fs.fstatSync(fd);
99
+ if (!st.isFile()) return { error: 'not a regular file' };
100
+ if (st.size > maxBytes) return { error: `larger than ${maxBytes} bytes` };
101
+ const buffer = Buffer.alloc(maxBytes + 1);
102
+ let length = 0;
103
+ for (let n = -1; n !== 0 && length <= maxBytes; length += n) n = fs.readSync(fd, buffer, length, buffer.length - length, null);
104
+ return length > maxBytes ? { error: `larger than ${maxBytes} bytes` } : { text: buffer.subarray(0, length).toString('utf8') };
105
+ } catch (error ) {
106
+ return error?.code === 'ENOENT' ? { missing: true } : { error: error?.message || String(error) };
107
+ } finally {
108
+ if (fd !== undefined) { try { fs.closeSync(fd); } catch { /* the read is done */ } }
109
+ }
110
+ }
111
+
112
+ function parseRecordLines(file ) {
113
+ const read = readBounded(file, MAX_RECORD_BYTES);
114
+ if ('error' in read) return { error: read.error };
115
+ const records = [];
116
+ for (const [index, line] of ('text' in read ? read.text : '').split('\n').entries()) {
117
+ if (!line.trim()) continue;
118
+ try { records.push(JSON.parse(line)); } catch (error ) { return { error: `line ${index + 1}: ${error.message}` }; }
119
+ }
120
+ return { records };
121
+ }
122
+
123
+
124
+
125
+
126
+ /**
127
+ * The slug's orchestration run in either record layout (flat `<id>.json` whose run names the slug or
128
+ * whose id is the slug, or `<slug>/<id>.json`), through the runtime's own listing so every read is
129
+ * bounded. `none` only when no layout holds one; every uncertain case is `ambiguous` or `unreadable`.
130
+ * The run's own `taskId` keys its verify records; a run that names none falls back to the slug.
131
+ */
132
+ function lookupRun(stateRoot , slug ) {
133
+ const { orchestrationRuns, selectRunEntries } = require('../implementation-gate.js');
134
+ const store = path.join(stateRoot, '.doflow', 'state', 'orchestration');
135
+ // The runtime's listing treats a folder it cannot read as empty, which would read as no run. ENOENT is
136
+ // the one error that means absent; any other error on the store or on `<slug>/` is unreadable.
137
+ const listDir = (dir ) => {
138
+ try { return { entries: fs.readdirSync(dir, { withFileTypes: true }) }; } catch (error ) {
139
+ return error?.code === 'ENOENT' ? { entries: [] } : { error: `${dir} cannot be listed (${error?.code || error?.message || String(error)})` };
140
+ }
141
+ };
142
+ const flat = listDir(store);
143
+ if ('error' in flat) return { status: 'unreadable', message: flat.error };
144
+ const nested = listDir(path.join(store, slug));
145
+ if ('error' in nested) return { status: 'unreadable', message: nested.error };
146
+ // A FIFO or device at a run path is not listed by the runtime (it lists regular files and links), so it
147
+ // would read as absent; a non-regular `.json` entry in the flat layer or in `<slug>/` could be this
148
+ // slug's run and cannot be attributed, so it is unreadable.
149
+ const odd = (dir , entries ) => {
150
+ const entry = entries.find((e ) => e.name.endsWith('.json') && !e.isFile() && !e.isSymbolicLink() && !e.isDirectory());
151
+ return entry ? path.join(dir, entry.name) : null;
152
+ };
153
+ const strange = odd(store, flat.entries) ?? odd(path.join(store, slug), nested.entries);
154
+ if (strange) return { status: 'unreadable', message: `${strange} is not a regular file` };
155
+ const listed = orchestrationRuns({ stateRoot });
156
+ if (!listed.ok) return { status: 'unreadable', message: `the run records could not be listed (${listed.error})` };
157
+ const mine = selectRunEntries(listed.entries).filter((entry ) => {
158
+ const namespaced = path.relative(path.join(entry.root, '.doflow', 'state', 'orchestration'), entry.file).split(path.sep).length > 1;
159
+ // `slug` names the feature like `featureSlug` does (the runtime's flatRecordIsOurs honours both).
160
+ const named = typeof entry.record?.featureSlug === 'string' && entry.record.featureSlug !== '' ? null : entry.record?.slug;
161
+ return entry.slug === slug || (typeof named === 'string' && named === slug)
162
+ || (!namespaced && path.basename(entry.file, '.json') === slug);
163
+ });
164
+ if (mine.length === 0) {
165
+ // No entry names this slug. A flat run of this store that could not be read (io/unparseable, or a
166
+ // link) cannot be attributed to any slug, so it could be this one's: fail closed and name the file.
167
+ const blind = selectRunEntries(listed.entries).find((entry ) => entry.error && entry.origin === 'current'
168
+ && path.dirname(entry.file) === store);
169
+ if (blind) return { status: 'unreadable', message: `${blind.file} is ${blind.error === 'io' ? 'not a readable regular file' : 'not valid JSON'} and cannot be ruled out as the run of ${slug}` };
170
+ return { status: 'none' };
171
+ }
172
+ if (mine.length > 1) return { status: 'ambiguous', message: `more than one run belongs to ${slug}: ${mine.map((e ) => e.file).join(', ')}` };
173
+ const [entry] = mine;
174
+ if (entry.error) return { status: 'unreadable', message: `${entry.file} is ${entry.error === 'io' ? 'not a readable regular file' : 'not valid JSON'}` };
175
+ const { taskClass, taskId } = entry.record ?? {};
176
+ if (typeof taskClass !== 'string' || !taskClass) return { status: 'unreadable', message: `${entry.file} names no taskClass` };
177
+ if (taskId !== undefined && !validSlug(taskId)) return { status: 'unreadable', message: `${entry.file} has a task id that is not a plain name` };
178
+ return { status: 'found', taskId: taskId ?? slug, taskClass };
179
+ }
180
+
181
+ // The settings keys `resolveProtocol` reads (119 IC-006), by their resolution's value and origin layer.
182
+ const PROTOCOL_SETTINGS = ['review.preset', 'review.rounds', 'review.sameFamily', 'review.crossFamilyRereview',
183
+ 'review.extraLenses', 'review.blockerClauses', 'debate.preset'];
184
+
185
+ /**
186
+ * 119 FR-008: the two inputs `resolveProtocol` turns into `enforcement`: `agentAccess` (the access of every agent
187
+ * the registry seats) and `readOnlyLimits` (each harness's `agentLimits.readOnly`, registry order). The access comes
188
+ * from core/registry/workflows.json `agents.access` (G63 holds it equal to the spec frontmatter), never from
189
+ * core/shared/agent-specs: an installed runtime ships core/registry only. An agent whose access cannot be read is
190
+ * `unknown`, never `write` and never a refusal: the seat binding stands and its enforcement rows say unknown. A
191
+ * harness registry that cannot be read, or that lacks a value, is an error for the caller to refuse with.
192
+ */
193
+ function loadEnforcementInputs(registry , { harnessesFile = path.join(REPO_ROOT, 'core', 'registry', 'harnesses.json'), workflowsFile = path.join(REPO_ROOT, 'core', 'registry', 'workflows.json') } = {})
194
+ {
195
+ const fail = (message ) => ({ error: 'protocols-invalid', message });
196
+ let declared = null;
197
+ try { declared = JSON.parse(fs.readFileSync(workflowsFile, 'utf8'))?.agents?.access; } catch { declared = null; }
198
+ const agentAccess = {};
199
+ for (const preset of Object.values (registry?.presets ?? {})) {
200
+ for (const seat of preset?.seats ?? []) {
201
+ const access = declared && Object.hasOwn(declared, seat.agent) ? declared[seat.agent] : undefined;
202
+ agentAccess[seat.agent] = access === 'read-only' || access === 'write' ? access : 'unknown';
203
+ }
204
+ }
205
+ let harnesses ;
206
+ try { harnesses = JSON.parse(fs.readFileSync(harnessesFile, 'utf8')).harnesses; } catch {
207
+ return fail(`the harnesses registry (core/registry/harnesses.json) cannot be read`);
208
+ }
209
+ if (!Array.isArray(harnesses) || harnesses.length === 0) return fail('the harnesses registry lists no harness');
210
+ const readOnlyLimits = [];
211
+ for (const harness of harnesses) {
212
+ const limit = harness?.agentLimits?.readOnly;
213
+ if (typeof harness?.id !== 'string' || (limit?.status !== 'native' && limit?.status !== 'unenforced')) {
214
+ return fail(`harness '${harness?.id}' has no agentLimits.readOnly.status of native or unenforced`);
215
+ }
216
+ readOnlyLimits.push({ harness: harness.id, status: limit.status, reason: typeof limit.reason === 'string' ? limit.reason : null });
217
+ }
218
+ return { agentAccess, readOnlyLimits };
219
+ }
220
+
221
+ /**
222
+ * The gate policy a review resolves its seat floor from: the shipped gates.json narrowed by the project's
223
+ * `.doflow/gates.override.json` (read as roadmap gate-b reads it, 119 IC-005). `override` says whether one was applied;
224
+ * an unreadable or widening override is `ok: false` with the loader's reason. Shared with `doflow rules`.
225
+ */
226
+ function loadGatePolicy(cwd )
227
+ {
228
+ const { projectLayerSource } = require('../settings/project-layers.js');
229
+ const layers = projectLayerSource(cwd, fs);
230
+ const overrideFile = layers.layersFrom ? path.join(layers.layersFrom, 'gates.override.json') : null;
231
+ let override = null;
232
+ if (overrideFile) {
233
+ try { override = fs.readFileSync(overrideFile, 'utf8'); } catch (error ) { if (error?.code !== 'ENOENT') override = ''; }
234
+ }
235
+ const loaded = loadPolicy(fs.readFileSync(path.join(REPO_ROOT, 'core', 'registry', 'gates.json'), 'utf8'), override);
236
+ if (!loaded.ok) return { ok: false, reason: String(loaded.reason), overrideFile };
237
+ return { ok: true, policy: loaded.policy, override: override === null ? 'none' : 'applied', overrideFile };
238
+ }
239
+
240
+ /**
241
+ * The inputs `resolveProtocol` needs beyond the change facts: the project's settings as the plain `settingsView`
242
+ * the resolver reads (`{ key: { value, layer } }`, 119 L-B8), whether the review is `unattended` (autopilot is not
243
+ * is on, so the class seat floor binds; maint-thin), the gate policy (the shipped gates.json narrowed by the
244
+ * project's `.doflow/gates.override.json`, read as roadmap gate-b reads it, 119 IC-005; null when there is no task class, so
245
+ * no floor), the protocols registry and the enforcement inputs (`loadEnforcementInputs`). A base, override or registry the loaders reject is a refusal carrying the loader's
246
+ * reason (and the override file). Shared with the `protocol` verb.
247
+ */
248
+ function loadProtocolInputs(taskClass , cwd , env = process.env)
249
+ {
250
+ const settingsView = {};
251
+ try {
252
+ const settings = require('../settings/resolve.js').loadSettings({ projectRoot: path.resolve(cwd), env });
253
+ for (const key of PROTOCOL_SETTINGS) {
254
+ const resolution = settings.get(key);
255
+ // C-6: an invalid project or local review.* candidate would silently fall back to the default: refuse instead.
256
+ // A preset key refuses an invalid user candidate too.
257
+ const invalid = key.endsWith('.preset') ? invalidPresetCandidate(resolution)
258
+ : key.startsWith('review.') ? invalidProjectCandidate(resolution) : undefined;
259
+ if (invalid) return { error: 'settings-unavailable', message: `${key} in ${invalid.source}: ${invalid.reason}` };
260
+ settingsView[key] = { value: resolution.value, layer: resolution.origin.layer };
261
+ }
262
+ } catch (error ) {
263
+ return { error: 'settings-unavailable', message: error?.message || String(error) };
264
+ }
265
+ let gatePolicy = null;
266
+ if (taskClass) {
267
+ const loaded = loadGatePolicy(cwd);
268
+ if (!loaded.ok) {
269
+ return { error: String(loaded.reason).split(':')[0], message: `${loaded.reason} (core/registry/gates.json narrowed by ${loaded.overrideFile ?? 'no project override'})` };
270
+ }
271
+ gatePolicy = loaded.policy;
272
+ }
273
+ const protocols = require('./protocols.js').loadProtocols();
274
+ if (!protocols.ok) return { error: 'protocols-invalid', message: protocols.errors.join('; ') };
275
+ const enforcement = loadEnforcementInputs(protocols.registry);
276
+ if ('error' in enforcement) return enforcement;
277
+ // maint-thin: the class seat floor binds unless autopilot is verifiably off (its policy gate-b may decide the merge
278
+ // unattended); a person-attended review follows the preset. Uncertain reads as unattended (see reviewUnattended).
279
+ const unattended = require('../settings/autopilot.js').reviewUnattended(env);
280
+ return { gatePolicy, unattended, registry: protocols.registry, settingsView, ...enforcement };
281
+ }
282
+
283
+ /** The decision model's opinion on the decided depth. Never throws: any failure is `unavailable`. */
284
+ async function askAdvice({ decision, risk, taskClass, slug, files, cwd, env, adviceClient } ) {
285
+ try {
286
+ const { loadAdviceRegistry } = require('../advice/registry.js');
287
+ const { loadStorageRegistry } = require('../storage/registry.js');
288
+ const { resolveRoots } = require('../storage/roots.js');
289
+ const { resolveSetting } = require('../advice/config.js');
290
+ const { materializeKind } = require('../advice/kinds.js');
291
+ const { createAdviceClient } = require('../advice/client.js');
292
+ const loaded = loadAdviceRegistry({ repoRoot: REPO_ROOT });
293
+ if (!loaded.ok) return { status: 'unavailable', pick: null };
294
+ const roots = resolveRoots({ registry: loadStorageRegistry({ repoRoot: REPO_ROOT }), projectRoot: path.resolve(cwd), env });
295
+ if (resolveSetting({ adviceRegistry: loaded.registry, roots, env }).state !== 'on') return { status: 'off', pick: null };
296
+ const built = materializeKind('review-depth', { adviceRegistry: loaded.registry });
297
+ if (!built.ok) return { status: 'unavailable', pick: null };
298
+ const client = adviceClient || createAdviceClient({ adviceRegistry: loaded.registry, roots });
299
+ const state = [`task class: ${taskClass ?? 'none'}`, `risk: ${risk}`, `scope: ${decision.scope}`,
300
+ `changed files: ${files ?? 'unknown'}`, `decided depth: ${decision.depth}`].join('\n');
301
+ // record: false keeps the verb write-free (IC-001); the pick is not stored.
302
+ const { resolveShadowConfig } = require('../advice/shadow-config.js');
303
+ const shadow = resolveShadowConfig('review-depth', { projectRoot: path.resolve(cwd), env, roots });
304
+ if (!shadow.kindSet) {
305
+ // advice.kinds.review-depth unset: ask the client directly, as before the dispatcher. A chosen rollout profile
306
+ // caps it: off asks nothing, and shadow asks nothing too, since a write-free verb could not record the answer.
307
+ const { profileAdvisorCap, unsetKindPath } = require('../advice/mode.js');
308
+ const route = unsetKindPath(profileAdvisorCap({ projectRoot: path.resolve(cwd), env }));
309
+ if (route === 'off') return { status: 'off', pick: null };
310
+ if (route === 'shadow') return { status: 'shadow', pick: null, reason: null };
311
+ const result = await client.ask({
312
+ questionSet: { state, questions: [built.question] },
313
+ kind: 'review-depth', staticAnswer: decision.depth, caller: 'review', taskId: slug, record: false,
314
+ });
315
+ const pick = result?.answers?.[0]?.pick;
316
+ if (result?.status === 'answered' && (pick === 'single' || pick === 'deep')) return { status: 'answered', pick };
317
+ return { status: result?.status === 'off' ? 'off' : 'unavailable', pick: null };
318
+ }
319
+ const { interceptDecisionPoint } = require('../advice/shadow-dispatcher.js');
320
+ const intercepted = await interceptDecisionPoint({
321
+ kind: 'review-depth', caller: 'review', state, staticPick: decision.depth,
322
+ questionSet: { state, questions: [built.question] }, adviceRegistry: loaded.registry,
323
+ taskId: slug, projectRoot: path.resolve(cwd), env, record: false,
324
+ }, { adviceClient: client, roots, shadowConfig: shadow });
325
+ // Only act applies a pick, and it is the decision after the raise-only rule.
326
+ const pick = intercepted.effectiveDecision;
327
+ if (intercepted.mode === 'act' && intercepted.advisorPick && (pick === 'single' || pick === 'deep')) return { status: 'answered', pick };
328
+ if (intercepted.mode === 'shadow') return { status: 'shadow', pick: null, reason: intercepted.warning || null };
329
+ return { status: intercepted.mode === 'off' ? 'off' : 'unavailable', pick: null };
330
+ } catch {
331
+ return { status: 'unavailable', pick: null };
332
+ }
333
+ }
334
+
335
+ function renderText(result , nextCommand ) {
336
+ const lines = [];
337
+ for (const [key, value] of Object.entries(result)) {
338
+ if (value === null || value === undefined) continue;
339
+ if (key === 'protocol') lines.push(...protocolLines(value));
340
+ else if (key === 'authors') {
341
+ const formatted = Array.isArray(value) && value.length
342
+ ? value.map((a ) => `${a.kind}:${a.harness}/${a.model}(${a.channel})`).join(' ')
343
+ : 'none';
344
+ lines.push(`authors: ${formatted}`);
345
+ } else if (key === 'forbidden') {
346
+ const formatted = Array.isArray(value) && value.length
347
+ ? value.map((p ) => `(${p[0]}, ${p[1]})`).join(', ')
348
+ : 'none';
349
+ lines.push(`forbidden: ${formatted}`);
350
+ } else if (Array.isArray(value)) lines.push(`${key}: ${value.length ? value.join(',') : 'none'}`);
351
+ else if (typeof value === 'object') {
352
+ lines.push(`${key}: ${Object.entries(value).filter(([, v]) => v !== null).map(([k, v]) => `${k}=${v}`).join(' ')}`);
353
+ } else lines.push(`${key}: ${value}`);
354
+ }
355
+ if (nextCommand) lines.push(nextCommand);
356
+ return lines;
357
+ }
358
+
359
+ function emit(out , json , result , nextCommand ) {
360
+ if (json) out(JSON.stringify(result));
361
+ else for (const line of renderText(result, nextCommand)) out(line);
362
+ }
363
+
364
+ /**
365
+ * IC-001. Exit 0 applicable and ok, or not applicable; 1 refused or records-unreadable.
366
+ * Usage errors (2) are decided by the caller before this runs.
367
+ */
368
+ async function handleReviewStart({ slug, base, preset, json = false, cwd, env, resolve, adviceClient, out } ) {
369
+ try {
370
+ const { WorkflowEngine } = require('../workflow-engine.js');
371
+ const { loadVerificationRegistry, reviewDepthPolicy } = require('../verification/registry.js');
372
+ const { verificationRunRecordFile, verificationRunsRoot } = require('../verification/engine.js');
373
+ const { resolveExplicitBaseline, resolveIntegrationBase } = require('../verification/scope-bound.js');
374
+
375
+ const registry = loadVerificationRegistry({ repoRoot: REPO_ROOT });
376
+ const riskPolicy = reviewDepthPolicy(registry);
377
+ const defaultRisk = registry.policy.defaultRiskLevel;
378
+
379
+ const result = {
380
+ schema: 1, slug: null, taskClass: null, applicable: false, notApplicable: null, head: null, clean: null,
381
+ ok: true, reasons: [], depth: null, seats: null, risk: defaultRisk, riskSource: 'default', protocol: null, protocolDigest: null, depthBasis: null,
382
+ advice: { status: 'off', pick: null },
383
+ authors: [], forbidden: [],
384
+ };
385
+ // An uncertain applicability is a refusal with a named reason, never a silent not-applicable.
386
+ const refuse = (error , message ) => {
387
+ // applicable is null on every error refusal.
388
+ out(json ? JSON.stringify({ ...result, applicable: null, ok: false, error, message }) : `review start: ${error}: ${message}`);
389
+ return finishRuntime(1);
390
+ };
391
+
392
+ let resolvedSlug = typeof slug === 'string' ? slug : null;
393
+ if (resolvedSlug === null) {
394
+ let feature ;
395
+ try { feature = resolve({ projectRoot: cwd, slug: null }); } catch (error ) {
396
+ return refuse('feature-unresolved', `the active feature could not be resolved: ${error?.message || String(error)}`);
397
+ }
398
+ // Only a resolver that names no active feature (or returns nothing) is a known absence; any other
399
+ // resolver error, or a slug that is not a plain name, is an uncertain applicability.
400
+ if (feature && typeof feature === 'object') {
401
+ if (feature.error && feature.error !== 'no-active-feature') {
402
+ return refuse('feature-unresolved', `${String(feature.error)}: ${feature.message ?? 'the active feature could not be resolved'}`);
403
+ }
404
+ if (!feature.error) {
405
+ const candidate = feature.paths?.feature_slug;
406
+ if (!validSlug(candidate)) return refuse('feature-unresolved', `invalid-slug: the resolved feature slug ${JSON.stringify(candidate ?? null)} is not a plain feature name`);
407
+ resolvedSlug = candidate;
408
+ }
409
+ }
410
+ }
411
+ result.slug = resolvedSlug;
412
+
413
+ let taskClass = null;
414
+ let verifyId = null;
415
+ if (resolvedSlug === null) result.notApplicable = 'no-feature';
416
+ else {
417
+ const run = lookupRun(verificationRunsRoot(cwd), resolvedSlug);
418
+ if (run.status === 'ambiguous') return refuse('run-ambiguous', run.message);
419
+ if (run.status === 'unreadable') return refuse('run-unreadable', run.message);
420
+ if (run.status === 'found') {
421
+ taskClass = run.taskClass;
422
+ verifyId = run.taskId;
423
+ result.taskClass = taskClass;
424
+ const classes = new WorkflowEngine({ repoRoot: REPO_ROOT }).classes;
425
+ if (!Object.hasOwn(classes ?? {}, taskClass)) return refuse('class-unknown', `task class '${taskClass}' is not in the workflows data`);
426
+ if (!reviewApplies(classes, taskClass)) result.notApplicable = 'class-exempt';
427
+ } else result.notApplicable = 'no-run';
428
+ }
429
+ result.applicable = result.notApplicable === null;
430
+
431
+ let nextCommand = null;
432
+ if (result.applicable) {
433
+ const state = readHeadState(cwd, env);
434
+ result.head = state.head;
435
+ result.clean = state.clean;
436
+ const parsed = parseRecordLines(verificationRunRecordFile(verificationRunsRoot(cwd), verifyId ));
437
+ if ('error' in parsed) return refuse('records-unreadable', parsed.error);
438
+ result.reasons = reviewPrecondition({ records: parsed.records, head: state.head, clean: state.clean, slug: verifyId });
439
+ if (result.reasons.length) {
440
+ result.ok = false;
441
+ nextCommand = `doflow verify --action report --task-id ${verifyId}${typeof base === 'string' ? ` --baseline-ref ${base}` : ''}`;
442
+ emit(out, json, result, nextCommand);
443
+ return finishRuntime(1);
444
+ }
445
+ Object.assign(result, riskOfRecords({ records: parsed.records, head: state.head, slug: verifyId, levels: registry.riskLevels, defaultRisk }));
446
+ }
447
+
448
+ let primary = null;
449
+ if (result.applicable) {
450
+ const { resolvePrimary } = require('../context/primary.js');
451
+ try {
452
+ primary = resolvePrimary({ projectRoot: cwd ? path.resolve(cwd) : process.cwd(), env });
453
+ if (primary?.state === 'refused') {
454
+ const msg = primary.warnings?.length ? primary.warnings.join('; ') : (primary.reason || 'primary is contradicted');
455
+ return refuse('primary-refused', msg);
456
+ }
457
+ } catch {}
458
+ }
459
+
460
+ let baseSha = null;
461
+ if (typeof base === 'string') {
462
+ const explicit = resolveExplicitBaseline({ cwd, ref: base });
463
+ baseSha = explicit.ok ? explicit.sha : null;
464
+ } else {
465
+ const integration = resolveIntegrationBase({ cwd });
466
+ baseSha = typeof integration.mergeBase === 'string' && integration.mergeBase ? integration.mergeBase : null;
467
+ }
468
+ const paths = changedPaths(cwd, env, baseSha);
469
+ const inputs = loadProtocolInputs(taskClass, cwd, env);
470
+ if ('error' in inputs) return refuse(inputs.error, inputs.message);
471
+
472
+ if (result.applicable) {
473
+ const { authoringIdentities } = require('./identity.js');
474
+ const authors = authoringIdentities({
475
+ slug: resolvedSlug ?? undefined,
476
+ cwd,
477
+ env,
478
+ primary,
479
+ });
480
+ result.authors = authors;
481
+
482
+ const inv8 = inputs.registry?.invariants?.find((inv ) => inv.id === 'INV-8');
483
+ const authorKinds = Array.isArray(inv8?.authorKinds) ? inv8.authorKinds : ['subagent', 'sequential'];
484
+ const forbidden = [];
485
+ const seenForbidden = new Set ();
486
+ for (const a of authors) {
487
+ if (!authorKinds.includes(a.kind)) continue;
488
+ if (!a.harness || !a.model || a.model === 'unknown') continue;
489
+ const key = `${a.harness}:${a.model}`;
490
+ if (!seenForbidden.has(key)) {
491
+ seenForbidden.add(key);
492
+ forbidden.push([a.harness, a.model]);
493
+ }
494
+ }
495
+ result.forbidden = forbidden;
496
+ }
497
+ const scope = pathScope(riskPolicy, paths);
498
+ const floor = inputs.unattended ? gateMinSeats(inputs.gatePolicy, taskClass) : 0;
499
+ const facts = { kind: 'review', taskClass, risk: result.risk, paths, runPreset: preset ?? null, ...inputs, riskPolicy };
500
+ const REFUSALS = {
501
+ 'preset-unknown': `--preset '${preset}' is not a review preset`,
502
+ 'preset-looser': `--preset '${preset}' is looser than the protocol this change needs; a run preset can only raise it`,
503
+ 'registry-incomplete': 'the protocols registry has no review preset for the decided depth',
504
+ };
505
+ const refuseResolution = (reason ) => refuse(reason, REFUSALS[reason] ?? reason);
506
+ // Once without advice for the depth the advice question is asked about, then with the pick.
507
+ const decided = resolveProtocol({ ...facts, advicePick: null });
508
+ if (!decided.ok) return refuseResolution(decided.reason);
509
+ const advice = await askAdvice({ decision: { depth: decided.protocol.depth, scope }, risk: result.risk, taskClass, slug: resolvedSlug, files: paths ? paths.length : null, cwd, env, adviceClient });
510
+ const resolved = resolveProtocol({ ...facts, advicePick: advice.pick });
511
+ if (!resolved.ok) return refuseResolution(resolved.reason);
512
+ const { protocol } = resolved;
513
+ const raisedBy = ({ floor: 'gate-floor', advice: 'advice', run: 'run', user: 'setting', project: 'setting', local: 'setting', env: 'setting' } )[protocol.origins.id]
514
+ ?? (protocol.depth === 'single' ? null : 'policy');
515
+ result.depth = protocol.depth;
516
+ result.seats = protocol.seats.length;
517
+ result.protocol = protocol;
518
+ result.protocolDigest = protocolDigest(protocol);
519
+ result.depthBasis = { scope, files: paths ? paths.length : null, base: paths ? baseSha : null, floor, raisedBy };
520
+ result.advice = advice;
521
+ emit(out, json, result, null);
522
+ return finishRuntime(0);
523
+ } catch (error ) {
524
+ captureCaught(error, 'review', 1);
525
+ console.error(`[ERROR] review start: ${error?.message || String(error)}`);
526
+ return finishRuntime(1);
527
+ }
528
+ }
529
+
530
+ module.exports = { reviewPrecondition, handleReviewStart, loadProtocolInputs, loadEnforcementInputs, loadGatePolicy, validSlug };