@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,254 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ const path = require('node:path');
6
+ const crypto = require('node:crypto');
7
+ const nodeFs = require('node:fs');
8
+ const { SAFE_TOKEN, telemetryOff, TelemetryWriter } = require('../trace/telemetry.js');
9
+ const { resolveRunsLocation } = require('../trace/ledger.js');
10
+ const { DISPATCH_RUN_ID } = require('../dispatch/store.js');
11
+ const { levelOf, v0Applies, v0Enabled, loadVerificationRegistry } = require('./registry.js');
12
+
13
+ let defaultRegistry = null;
14
+ function getDefaultRegistry() {
15
+ if (!defaultRegistry) {
16
+ try {
17
+ defaultRegistry = loadVerificationRegistry();
18
+ } catch {
19
+ defaultRegistry = null;
20
+ }
21
+ }
22
+ return defaultRegistry;
23
+ }
24
+
25
+
26
+ /**
27
+ * Verify telemetry (088 C.1, IC-006, DD-4): one `verify.run` line per verify run, one `verify.tier`
28
+ * per report tier and one `verify.check` per check, written through `TelemetryWriter`.
29
+ *
30
+ * The report is never touched: timing comes from the runner's `metrics` WeakMap (keyed by each
31
+ * check result object), so `verify --json` is the same bytes with telemetry on, off or faulted
32
+ * (FR-002, NFR-005, NFR-007). Records are built with every design field; the writer keeps only
33
+ * the keys `core/registry/telemetry.json` declares for the kind, so a later phase widens the lines
34
+ * by declaring keys, not by changing this module. Fields whose source does not exist yet are null:
35
+ * `level` until G.1, `v0` until G.2, `check_key` and `shared` until E.2.
36
+ *
37
+ * Where: `DOFLOW_CONFIG_DIR` when set, even over a nearer store; else the nearest `.doflow` above
38
+ * `start` that holds `state/runs`, so a `.doflow` without a ledger (one left by an older runtime's
39
+ * cwd-rooted verification logs) is walked past. With no project store nothing is written: no global
40
+ * fallback, so the lines stay under the project's state (NFR-006), as the bridge's hook lines do.
41
+ */
42
+
43
+
44
+
45
+ /** ISO 8601 with ms for an epoch-ms number, else undefined (the writer then stamps its own clock). */
46
+ function isoAt(ms ) {
47
+ return typeof ms === 'number' && Number.isFinite(ms) ? new Date(ms).toISOString() : undefined;
48
+ }
49
+
50
+ /**
51
+ * The records for one verify run, in write order: run, then each tier followed by its checks.
52
+ * Pure; a value that fails its registry type is nulled by the writer, never here.
53
+ * @param {Object} input
54
+ * @param {Object} input.report a `runContract` report, or the dispatch runner's `{status, startedAt, finishedAt, tiers: []}`
55
+ * @param {WeakMap} [input.metrics] check result → `{ startedAt, durationMs }`
56
+ * @param {{verifyId: string, source: string, taskId: *, risk: *, exitCode: *}} input.ctx
57
+ * @returns {Array<Object>}
58
+ */
59
+ function buildVerifyRecords({ report, metrics = null, ctx } ) {
60
+ const verifyId = ctx.verifyId;
61
+ const runStart = Date.parse(report.startedAt);
62
+ const runEnd = Date.parse(report.finishedAt);
63
+ // Timestamp of a record with no start of its own (a tier with no check, an in-run duplicate):
64
+ // the latest instant seen so far, so line timestamps never go backwards.
65
+ let cursor = Number.isFinite(runStart) ? runStart : undefined;
66
+ // Check stamps come from ONE wall-clock base plus the monotonic clock the durations already use: the
67
+ // first executed check pins `anchor`, and a later check is stamped anchor.wall + (its monotonic start -
68
+ // anchor.mono), so a wall-clock step mid-run neither reorders checks nor collapses them onto one instant.
69
+ // A check with no monotonic start (reused, or a clock that could not be read) keeps its wall start.
70
+ let anchor = null;
71
+ // The latest timestamp written so far: a record with no start of its own sits at the cursor, which
72
+ // advances by a rounded duration and can pass the next real start by a millisecond; no stamp goes back.
73
+ let latest = cursor;
74
+ const stamp = (ms ) => {
75
+ if (ms === undefined) return latest;
76
+ latest = latest === undefined ? ms : Math.max(latest, ms);
77
+ return latest;
78
+ };
79
+ let firstStamp ;
80
+ let checksRun = 0;
81
+ const body = [];
82
+ const commandKeys = new Map ();
83
+
84
+ for (const tier of Array.isArray(report.tiers) ? report.tiers : []) {
85
+ const checks = [];
86
+ let tierMs = null;
87
+ let tierStart ;
88
+ for (const check of Array.isArray(tier.checks) ? tier.checks : []) {
89
+ const timing = check && typeof check === 'object' && metrics ? metrics.get(check) : undefined;
90
+ let durationMs = null;
91
+ let at = cursor;
92
+ let checkKey = null;
93
+ let shared = null;
94
+ let sharedRef = null;
95
+ let tests = null;
96
+ let testsFailed = null;
97
+ let cpuMs = null;
98
+ let resourceBasis = null;
99
+ let failedFirst = null;
100
+
101
+ if (check && check.status === 'NOT_RUN') {
102
+ durationMs = null;
103
+ failedFirst = null;
104
+ } else if (timing) {
105
+ // A follower waited on another run and a reused check (089 IC-007) read a record: neither executed.
106
+ if (timing.shared !== 'follower' && timing.shared !== 'reused') checksRun += 1;
107
+ if (Number.isFinite(timing.startedAt)) {
108
+ at = timing.startedAt;
109
+ if (Number.isFinite(timing.startedMonoMs)) {
110
+ anchor = anchor ?? { wall: timing.startedAt, mono: timing.startedMonoMs };
111
+ at = Math.round(anchor.wall + (timing.startedMonoMs - anchor.mono));
112
+ // Nothing but the run line is written yet when the first check lands before it: lower the floor.
113
+ if (firstStamp === undefined) { firstStamp = at; if (latest !== undefined && at < latest) latest = at; }
114
+ }
115
+ }
116
+ if (Number.isFinite(timing.durationMs)) durationMs = Math.round(timing.durationMs);
117
+ if (at !== undefined) cursor = Math.max(cursor ?? at, at + (durationMs ?? 0));
118
+ checkKey = timing.checkKey ?? null;
119
+ shared = timing.shared ?? 'none';
120
+ sharedRef = timing.sharedRef ?? null;
121
+ tests = typeof timing.tests === 'number' ? timing.tests : null;
122
+ testsFailed = typeof timing.testsFailed === 'number' ? timing.testsFailed : null;
123
+ cpuMs = typeof timing.cpuMs === 'number' ? timing.cpuMs : null;
124
+ resourceBasis = timing.resourceBasis ?? null;
125
+ failedFirst = timing.failedFirst ?? (check?.failedFirst ? (check.failedFirst.status === 'FAIL' ? 'fail' : 'pass') : 'none');
126
+ if (check && check.command && checkKey) {
127
+ commandKeys.set(check.command, checkKey);
128
+ }
129
+ } else if (check && check.deduplicated) {
130
+ durationMs = 0; // reused the result of an identical check earlier in this run (S-10)
131
+ shared = 'in-run';
132
+ failedFirst = 'none';
133
+ checkKey = (check.command && commandKeys.get(check.command)) ?? null;
134
+ } else if (check) {
135
+ failedFirst = check.failedFirst ? (check.failedFirst.status === 'FAIL' ? 'fail' : 'pass') : 'none';
136
+ }
137
+ if (durationMs !== null) tierMs = (tierMs ?? 0) + durationMs;
138
+ at = stamp(at);
139
+ if (tierStart === undefined) tierStart = at;
140
+ checks.push({
141
+ kind: 'verify.check',
142
+ timestamp: isoAt(at),
143
+ verify_id: verifyId,
144
+ tier: tier.id,
145
+ status: check ? check.status : null,
146
+ exit_code: check ? (check.exitCode ?? null) : null,
147
+ duration_ms: durationMs,
148
+ tests,
149
+ tests_failed: testsFailed,
150
+ cpu_ms: cpuMs,
151
+ resource_basis: resourceBasis,
152
+ check_key: checkKey,
153
+ shared,
154
+ shared_ref: sharedRef,
155
+ failed_first: failedFirst,
156
+ });
157
+ }
158
+ const selection = tier.selection || {};
159
+ body.push({
160
+ kind: 'verify.tier',
161
+ timestamp: isoAt(tierStart ?? stamp(cursor)),
162
+ verify_id: verifyId,
163
+ tier: tier.id,
164
+ status: tier.status,
165
+ duration_ms: tierMs, // derived: the sum of its checks (IC-006 basis derived-sum)
166
+ selection_basis: selection.basis ?? null,
167
+ selected: selection.selectedCount ?? null,
168
+ total: selection.totalCount ?? null,
169
+ fallback: selection.fallback ?? null,
170
+ }, ...checks);
171
+ }
172
+
173
+ const dispatchRun = ctx.dispatchRun ?? ctx.dispatch_run;
174
+ const reg = ctx.registry || getDefaultRegistry();
175
+ const risk = ctx.risk ?? null;
176
+ const level = ctx.level ?? (risk ? levelOf(reg, risk) : null);
177
+ const applies = ctx.v0Applies !== undefined ? Boolean(ctx.v0Applies) : (risk ? v0Applies(reg, risk) : false);
178
+
179
+ let v0 = null;
180
+ if (ctx.source === 'dispatch') {
181
+ v0 = null;
182
+ } else if (ctx.v0 === 'off' || ctx.v0 === false) {
183
+ v0 = 'off';
184
+ } else if (ctx.v0 === 'on' || ctx.v0 === true) {
185
+ v0 = applies ? 'on' : null;
186
+ } else if (ctx.v0 === null) {
187
+ v0 = null;
188
+ } else {
189
+ const isEnabled = v0Enabled(ctx.env || process.env, ctx.configDir);
190
+ if (!isEnabled) {
191
+ v0 = 'off';
192
+ } else {
193
+ v0 = applies ? 'on' : null;
194
+ }
195
+ }
196
+
197
+ const run = {
198
+ kind: 'verify.run',
199
+ timestamp: isoAt(firstStamp !== undefined && Number.isFinite(runStart) ? Math.min(runStart, firstStamp) : runStart),
200
+ verify_id: verifyId,
201
+ source: ctx.source,
202
+ task: typeof ctx.taskId === 'string' && SAFE_TOKEN.test(ctx.taskId) ? ctx.taskId : null,
203
+ dispatch_run: ctx.source === 'dispatch' && typeof dispatchRun === 'string' && DISPATCH_RUN_ID.test(dispatchRun)
204
+ ? dispatchRun
205
+ : null,
206
+ risk,
207
+ level,
208
+ status: report.status,
209
+ exit_code: ctx.exitCode ?? null,
210
+ // Wall time between the report's own stamps: the engine clock, not a monotonic one.
211
+ duration_ms: Number.isFinite(runStart) && Number.isFinite(runEnd) ? Math.max(0, runEnd - runStart) : null,
212
+ checks_run: checksRun,
213
+ v0,
214
+ };
215
+ return [run, ...body];
216
+ }
217
+
218
+ /**
219
+ * The directory the writer should walk up from, or null when no project store exists.
220
+ * @param {string} [start]
221
+ * @param {Object} env
222
+ * @param {Object} fsImpl
223
+ * @returns {string|null}
224
+ */
225
+ function storeStart(start , env , fsImpl ) {
226
+ const at = resolveRunsLocation({ start, env, fsImpl, containing: path.join('state', 'runs') });
227
+ if (at.origin === 'DOFLOW_CONFIG_DIR') return start || process.cwd(); // the writer reads the same variable
228
+ return at.origin === 'project' ? path.dirname(at.configDir) : null;
229
+ }
230
+
231
+ /**
232
+ * Writes the verify lines for one run. Never throws; any fault writes nothing (NFR-005).
233
+ * @param {Object} input
234
+ * @param {Object} input.report
235
+ * @param {WeakMap} [input.metrics]
236
+ * @param {{source: 'verb'|'dispatch', taskId: *, risk: *, exitCode: *}} input.ctx
237
+ * @param {Object} [input.env=process.env]
238
+ * @param {string} [input.start] the verify's working directory (walk-up origin)
239
+ * @param {Function} [input.now] writer clock; test seam
240
+ * @param {Object} [input.fsImpl] test seam
241
+ */
242
+ function emitVerifyTelemetry({ report, metrics = null, ctx, env = process.env, start, now, fsImpl = nodeFs } ) {
243
+ try {
244
+ if (telemetryOff(env)) return;
245
+ const from = storeStart(start, env, fsImpl);
246
+ if (from === null) return;
247
+ const records = buildVerifyRecords({ report, metrics, ctx: { env: ctx?.env || env, ...ctx, verifyId: crypto.randomBytes(8).toString('hex') } });
248
+ new TelemetryWriter({ env, fsImpl, start: from, ...(now ? { now } : {}) }).write(records);
249
+ } catch {
250
+ // Telemetry changes no outcome (FR-002, NFR-005).
251
+ }
252
+ }
253
+
254
+ module.exports = { buildVerifyRecords, emitVerifyTelemetry };
@@ -20,7 +20,7 @@ const nodeFs = require('node:fs');
20
20
  /** The guard lane of a `node --test` repository: structural checks that run with every selection. */
21
21
  const GUARD_DIR = 'test/guards';
22
22
 
23
- const NODE_TEST_FILE = /\.test\.(?:js|mjs|cjs)$/;
23
+ const NODE_TEST_FILE = /\.test\.(?:js|mjs|cjs|ts)$/;
24
24
  const PYTEST_FILE = /(?:^|\/)(?:test_[^/]*|[^/]*_test)\.py$/;
25
25
 
26
26
  const SKIPPED_DIRS = new Set(['node_modules', '.git', '.venv', 'venv', '__pycache__']);
@@ -97,14 +97,37 @@ function conventionFor(found ) {
97
97
  roots,
98
98
  isTest: (file ) => NODE_TEST_FILE.test(file) && underRoot(file),
99
99
  guardDir: roots.some((r ) => GUARD_DIR === r || GUARD_DIR.startsWith(`${r}/`)) ? GUARD_DIR : null,
100
+ found,
100
101
  };
101
102
  }
102
103
  if (found && found.source === 'pyproject.toml') {
103
- return { basis: 'changed-test-files', roots: ['.'], isTest: (file ) => PYTEST_FILE.test(file), guardDir: null };
104
+ return { basis: 'changed-test-files', roots: ['.'], isTest: (file ) => PYTEST_FILE.test(file), guardDir: null, found };
104
105
  }
105
106
  return null;
106
107
  }
107
108
 
109
+ /**
110
+ * Walks the test files for the toolchain described by `found`.
111
+ * Returns sorted unique repo-relative paths of regular files that satisfy `convention.isTest`.
112
+ * @param {Object} found the detected `testTargeted` entry
113
+ * @param {string} root project root
114
+ * @param {Object} [fsImpl]
115
+ * @returns {Array<string>}
116
+ */
117
+ function testFilesFor(found , root , fsImpl = nodeFs) {
118
+ const convention = conventionFor(found);
119
+ if (!convention) return [];
120
+ const files = new Set ();
121
+ for (const r of convention.roots) {
122
+ for (const file of walkFiles(fsImpl, root, r)) {
123
+ if (convention.isTest(file) && statOf(fsImpl, path.join(root, file))?.isFile()) {
124
+ files.add(file);
125
+ }
126
+ }
127
+ }
128
+ return [...files].sort();
129
+ }
130
+
108
131
  /**
109
132
  * @param {Object} input
110
133
  * @param {Object} input.found the detected `testTargeted` entry (`source`, `testDirs`)
@@ -126,10 +149,7 @@ function selectChangedTests({ found, observation, root, fsImpl = nodeFs, platfor
126
149
  const convention = conventionFor(found);
127
150
  if (!convention) return null;
128
151
 
129
- const total = new Set ();
130
- for (const r of convention.roots) {
131
- for (const file of walkFiles(fsImpl, root, r)) if (convention.isTest(file)) total.add(file);
132
- }
152
+ const total = new Set (testFilesFor(found, root, fsImpl));
133
153
  const totalCount = total.size;
134
154
  const fallback = (reason , selectedCount = 0) => ({
135
155
  pattern: null,
@@ -169,4 +189,44 @@ function selectChangedTests({ found, observation, root, fsImpl = nodeFs, platfor
169
189
  };
170
190
  }
171
191
 
172
- module.exports = { selectChangedTests, quotePath, GUARD_DIR };
192
+ /**
193
+ * Formats a sorted list of selected test files into a shell-quoted command argument pattern (IC-003).
194
+ * When guardDir is present and files under guardDir are included in selectedTests, they are replaced
195
+ * by a single quoted `${guardDir}/` token.
196
+ *
197
+ * @param {Object} input
198
+ * @param {Array<string>} input.selectedTests repo-relative test paths
199
+ * @param {string|null} [input.guardDir] optional guard directory (e.g. 'test/guards')
200
+ * @param {string} [input.platform] platform for path quoting
201
+ * @returns {string} shell-quoted argument string
202
+ */
203
+ function formatSelectedTestPattern({
204
+ selectedTests = [],
205
+ guardDir = null,
206
+ platform = process.platform,
207
+ }
208
+
209
+
210
+
211
+ ) {
212
+ const underGuard = (file ) => Boolean(guardDir && (file === guardDir || file.startsWith(`${guardDir}/`)));
213
+ const nonGuard = [...new Set(selectedTests.map(normalize))]
214
+ .filter((f ) => !underGuard(f))
215
+ .sort();
216
+ const asArgument = (file ) => (file.startsWith('-') ? `./${file}` : file);
217
+ const words = nonGuard.map((file ) => quotePath(asArgument(file), platform));
218
+ const hasGuard = Boolean(guardDir && (selectedTests.some(underGuard) || selectedTests.length === 0));
219
+ if (hasGuard) {
220
+ words.push(quotePath(`${guardDir}/`, platform));
221
+ }
222
+ return words.join(' ');
223
+ }
224
+
225
+ module.exports = {
226
+ selectChangedTests,
227
+ formatSelectedTestPattern,
228
+ quotePath,
229
+ GUARD_DIR,
230
+ testFilesFor,
231
+ conventionFor,
232
+ };
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ const { shippedWorkerIds, knownWorkerIds } = require('../../registry/worker-ids.js');
4
+ const { loadRegistry } = require('../../registry/index.js');
5
+ const { REPO_ROOT } = require('../../helper/repo-root.js');
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+ /** IC-009: resolve the usable workers and in-session permission from workers.enabled. */
18
+ function activeWorkers({ registry, view, env = process.env, projectRoot = null } = {}) {
19
+ const reg = registry || loadRegistry({ repoRoot: REPO_ROOT });
20
+ const known = knownWorkerIds({ registry: reg, view }).ids;
21
+ let res ;
22
+ try {
23
+ const { loadSettings } = require('../settings/resolve.js');
24
+ res = loadSettings({ projectRoot, env: { ...env } }).get('workers.enabled', 'project');
25
+ } catch {
26
+ return {
27
+ state: 'invalid', workers: [], inSession: false, dangling: [],
28
+ origin: { layer: 'default', source: null }, reason: 'unreadable', narrowedFrom: null,
29
+ };
30
+ }
31
+
32
+ const origin = { layer: res.origin.layer, source: res.origin.source };
33
+ if (res.state === 'invalid') {
34
+ const candidate = res.candidates.find((item ) =>
35
+ item.status === 'invalid' && item.layer === origin.layer && item.source === origin.source);
36
+ return {
37
+ state: 'invalid', workers: [], inSession: false, dangling: [],
38
+ origin, reason: candidate?.reason || 'invalid', narrowedFrom: null,
39
+ };
40
+ }
41
+ if (origin.layer === 'default') {
42
+ return {
43
+ state: 'unset', workers: shippedWorkerIds(reg), inSession: true, dangling: [],
44
+ origin, reason: null, narrowedFrom: null,
45
+ };
46
+ }
47
+
48
+ const value = res.value ;
49
+ const ids = value.filter((id) => id !== 'primary');
50
+ const user = origin.layer === 'project' || origin.layer === 'local'
51
+ ? res.candidates.find((item ) => item.layer === 'user' && item.value !== null && item.status === 'overridden')
52
+ : null;
53
+ return {
54
+ state: 'set',
55
+ workers: ids.filter((id) => known.includes(id)),
56
+ inSession: value.at(-1) === 'primary',
57
+ dangling: ids.filter((id) => !known.includes(id)),
58
+ origin,
59
+ reason: null,
60
+ narrowedFrom: user ? [...user.value] : null,
61
+ };
62
+ }
63
+
64
+ /** IC-009: refuse a worker outside the resolved usable set. */
65
+ function workerGate(active , id ) {
66
+ if (active.workers.includes(id)) return null;
67
+ return {
68
+ reason: active.state === 'invalid' ? 'workers-enabled-invalid' : 'worker-disabled',
69
+ harness: id,
70
+ enabled: [...active.workers],
71
+ origin: active.origin,
72
+ };
73
+ }
74
+
75
+ /** IC-009: refuse in-session work when primary is absent or the list is invalid. */
76
+ function inSessionGate(active ) {
77
+ if (active.inSession) return null;
78
+ if (active.state === 'invalid') {
79
+ return {
80
+ reason: 'workers-enabled-invalid',
81
+ text: 'workers-enabled-invalid: workers.enabled is invalid, do not run in-session',
82
+ };
83
+ }
84
+ return {
85
+ reason: 'no-enabled-worker',
86
+ text: 'no-enabled-worker: primary not enabled, do not run in-session',
87
+ };
88
+ }
89
+
90
+ module.exports = { activeWorkers, workerGate, inSessionGate };
@@ -0,0 +1,222 @@
1
+ 'use strict';
2
+
3
+ // Feature 081 C.1 (IC-021, IC-020 end state; DEC-005, DEC-038, DEC-040, DEC-046, DEC-047, DEC-050):
4
+ // the cancel sequence, shared by the `worker --action cancel` command and the supervisor's timeout.
5
+ //
6
+ // In order: (1) refusals before any write or signal, against the calling process's own identity;
7
+ // (2) the `cancelling` transition; (3) the tree escalation, outside every lock; (4) 080's attempt
8
+ // cancel, in process, with the caller's own `env` and `cwd` and no rebuilt identity, a refusal
9
+ // recorded and a throw stored as `disposition: 'error'`; (5) the end, always written once step 2
10
+ // applied. The escalation runs before the 080 cancel, so an 080 failure never leaves the tree alive.
11
+ // A child run (082 IC-015, DEC-028, DEC-041) shares its granter's 080 attempt, so step 4 is skipped
12
+ // for it on the user and timeout paths alike: `attemptCancel: { disposition: 'skipped', reason:
13
+ // 'child-run' }`, and ending a child never cancels its granter's attempt.
14
+ //
15
+ // This module makes no fs write call: every write goes through the run store's `transition`, and
16
+ // the 080 ledger is written only by 080's own `cancel` (G26 (b), IC-027).
17
+
18
+ const fs = require('node:fs');
19
+ const path = require('node:path');
20
+ const { readRuns, transition, RunStoreError } = require('./run-store.js');
21
+ const processTree = require('./process-tree.js');
22
+ const { runKeyFor, identityRefusal } = require('../task-run.js');
23
+ const attempts = require('../task-attempts.js');
24
+
25
+ /** 080's `SAFE_NAME`, which `--by` must match. */
26
+ const SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9._ -]*$/;
27
+ /** A `cancelling` run whose request is younger than this belongs to the canceller that holds it. */
28
+ const CANCEL_TAKEOVER_MS = 30000;
29
+ /** How long a cancel of a launching run waits for its supervisor: its own escalation of a
30
+ * SIGTERM-ignoring worker, both waits and both `ps` bounds (IC-021 step 3, DEC-040, DEC-064). */
31
+ const LAUNCH_WAIT_MS = processTree.KILL_GRACE_MS + processTree.KILL_FINAL_MS + 2 * processTree.PS_TIMEOUT_MS;
32
+ const SOURCES = Object.freeze(['user', 'timeout']);
33
+
34
+ /** Thrown inside a locked transition to abandon it with a refusal and no write. */
35
+ class Refused extends Error {
36
+
37
+ constructor(result ) {
38
+ super(result.reason);
39
+ this.result = result;
40
+ }
41
+ }
42
+
43
+ const defaultSleep = (ms ) => new Promise((resolve) => setTimeout(resolve, ms));
44
+
45
+ function refused(reason , runId , extra = {}) {
46
+ return { disposition: 'refused', reason, runId, ...extra };
47
+ }
48
+
49
+ /** `<root>/.doflow/state/worker-runs/<slug>.json` gives the lifecycle root and the slug: the 080
50
+ * ledger lives under the same root (data-model.md §1). */
51
+ function ledgerFor(file ) {
52
+ const root = path.resolve(path.dirname(file), '..', '..', '..');
53
+ const slug = path.basename(file, '.json');
54
+ return { root, slug, ledger: attempts.storeFile(root, slug) };
55
+ }
56
+
57
+ /** A `cancelling` run whose request is under 30 s old, or null. Pure. */
58
+ function inProgress(run , at ) {
59
+ if (run.phase !== 'cancelling') return null;
60
+ const requested = Date.parse(run.cancel?.requestedAt);
61
+ if (Number.isFinite(requested) && at.getTime() - requested < CANCEL_TAKEOVER_MS) {
62
+ return refused('cancel-in-progress', run.runId, { requestedAt: run.cancel.requestedAt });
63
+ }
64
+ return null;
65
+ }
66
+
67
+ const pidOf = (entry ) => (entry && typeof entry === 'object' ? entry.pid : entry);
68
+
69
+ function union(a = [], b = []) {
70
+ const seen = new Map ();
71
+ for (const entry of [...a, ...b]) if (!seen.has(pidOf(entry))) seen.set(pidOf(entry), entry);
72
+ return [...seen.values()];
73
+ }
74
+
75
+ /**
76
+ * IC-014's add-only rule (DEC-059): a recorded `supervisorCleanup` may only add to `cleanup`. An
77
+ * `unknown` status becomes `verified` only when `supervisorCleanup.status` is `verified`, becomes
78
+ * `survivors` when the supervisor found any, and otherwise stays `unknown` with its own reason; a
79
+ * `verified` one becomes `survivors` when the supervisor found any; the lists are unions.
80
+ */
81
+ function mergeCleanup(cleanup , supervisorCleanup ) {
82
+ if (!supervisorCleanup) return cleanup;
83
+ const survivors = union(cleanup?.survivors, supervisorCleanup.survivors);
84
+ const zombies = union(cleanup?.zombies, supervisorCleanup.zombies);
85
+ let status ;
86
+ if (survivors.length || cleanup?.status === 'survivors') status = 'survivors';
87
+ else if (cleanup?.status === 'verified' || supervisorCleanup.status === 'verified') status = 'verified';
88
+ else status = 'unknown';
89
+ const reason = status === 'unknown' ? cleanup?.reason ?? supervisorCleanup.reason ?? null : null;
90
+ return { status, reason, survivors, zombies };
91
+ }
92
+
93
+ /** Polls the store until the supervisor has exited or recorded `supervisorCleanup`, up to `budgetMs`. */
94
+ async function waitForSupervisor({ file, runId, supervisor, platform, budgetMs, sleep, clock } ) {
95
+ const until = clock().getTime() + budgetMs;
96
+ for (;;) {
97
+ const run = readRuns(file)[runId];
98
+ if (run?.supervisorCleanup) return { run, supervisorAlive: false };
99
+ const live = supervisor ? processTree.alive(supervisor, { platform }) : false;
100
+ if (live === false || clock().getTime() >= until) return { run, supervisorAlive: live };
101
+ await sleep(processTree.POLL_MS);
102
+ }
103
+ }
104
+
105
+ /** IC-021 step 3 for the record step 2 returned. */
106
+ async function endTree({ file, runId, run, platform, sleep, clock, escalate } ) {
107
+ if (platform === 'win32') {
108
+ if (run.worker) await escalate({ proc: run.worker, platform });
109
+ return { status: 'unknown', reason: 'win32', survivors: [], zombies: [] };
110
+ }
111
+ if (run.worker) return escalate({ proc: run.worker, platform });
112
+ // A starting run. With no `launchingAt` the supervisor's launch transition is refused and it
113
+ // spawns nothing (DEC-040).
114
+ if (!run.launchingAt) return { status: 'verified', reason: 'not-launched', survivors: [], zombies: [] };
115
+ // `launchingAt` and no `worker`: the supervisor may already have spawned. Its own `running`
116
+ // transition is refused, so it kills its worker and records `supervisorCleanup`; wait for that,
117
+ // then end the supervisor's tree when it still runs. The verdict stays `unknown` here: only a
118
+ // recorded `supervisorCleanup` resolves it, at step 5.
119
+ const waited = await waitForSupervisor({ file, runId, supervisor: run.supervisor, platform, budgetMs: LAUNCH_WAIT_MS, sleep, clock });
120
+ let survivors = [];
121
+ let zombies = [];
122
+ if (waited.supervisorAlive === true) {
123
+ const outcome = await escalate({ proc: run.supervisor, platform });
124
+ survivors = outcome.survivors;
125
+ zombies = outcome.zombies;
126
+ }
127
+ return { status: 'unknown', reason: 'worker-unrecorded', survivors, zombies };
128
+ }
129
+
130
+ /** 082 IC-015: what step 4 records for a child run, which has no 080 attempt of its own. */
131
+ const CHILD_SKIP = Object.freeze({ disposition: 'skipped', reason: 'child-run' });
132
+
133
+ /** IC-021 step 4: 080's cancel, its refusal recorded and its throw stored (DEC-046). */
134
+ function attemptCancelStep({ file, run, by, reason, env, cwd, clock, cancelImpl } ) {
135
+ const { root, slug, ledger } = ledgerFor(file);
136
+ try {
137
+ const answer = cancelImpl({
138
+ file: ledger, fsImpl: fs, roots: [...new Set([path.resolve(cwd), root])], slug,
139
+ task: run.task, token: run.token, by, reason, env, cwd, now: clock(),
140
+ });
141
+ return { disposition: answer?.disposition ?? 'error', reason: answer?.reason ?? null };
142
+ } catch (error ) {
143
+ return { disposition: 'error', reason: typeof error?.code === 'string' && error.code ? error.code : 'cancel-threw' };
144
+ }
145
+ }
146
+
147
+ /**
148
+ * IC-021 steps 1 to 5. `now` is a clock function (default `() => new Date()`) or a fixed Date.
149
+ * Refusals return `{ disposition: 'refused', reason, runId, ... }` with nothing written or
150
+ * signalled; success returns `{ disposition: 'cancelled', runId, endState, cleanup, attemptCancel,
151
+ * attemptCancelled }`, survivors included. `cancelImpl`, `escalate` and `sleep` default to the real
152
+ * 080 cancel, A.3's escalation and a timer, and exist for in-process tests.
153
+ */
154
+ async function cancelRun({
155
+ file, runId, by, reason, source = 'user', env = process.env, cwd = process.cwd(), platform = process.platform,
156
+ now = () => new Date(), cancelImpl = attempts.cancel, escalate = processTree.escalate, sleep = defaultSleep,
157
+ } ) {
158
+ if (!by || !SAFE_NAME.test(String(by))) throw new RunStoreError('usage', '--by is required: the name of the agent or person cancelling the run');
159
+ const why = typeof reason === 'string' ? reason.trim() : '';
160
+ if (!why) throw new RunStoreError('usage', '--reason is required: why the run is being cancelled');
161
+ if (!SOURCES.includes(source)) throw new RunStoreError('usage', `source '${source}' is not one of ${SOURCES.join(', ')}`);
162
+ const clock = typeof now === 'function' ? now : () => now;
163
+
164
+ // Step 1: the calling process's own identity, before anything is read for writing.
165
+ const key = runKeyFor(env, cwd);
166
+ const who = identityRefusal(key.identity);
167
+ if (who) return refused(who.reason, runId, { runKey: key.runKey });
168
+ const seen = readRuns(file)[runId];
169
+ if (!seen) return refused('unknown-run', runId);
170
+ if (seen.caller?.runKey !== key.runKey) return refused('foreign-run', runId, { runKey: seen.caller?.runKey ?? null });
171
+ if (seen.phase === 'ended') return refused('run-ended', runId, { endState: seen.end?.state ?? null });
172
+ const early = inProgress(seen, clock());
173
+ if (early) return early;
174
+
175
+ // Step 2: the same checks again under the lock, then `cancelling`.
176
+ const requestedAt = clock();
177
+ let marked ;
178
+ try {
179
+ marked = transition(file, runId, ['starting', 'running', 'cancelling'], (run ) => {
180
+ if (run.caller?.runKey !== key.runKey) throw new Refused(refused('foreign-run', runId, { runKey: run.caller?.runKey ?? null }));
181
+ const busy = inProgress(run, requestedAt);
182
+ if (busy) throw new Refused(busy);
183
+ run.phase = 'cancelling';
184
+ run.cancel = { requestedAt: requestedAt.toISOString(), by: String(by), reason: why, source };
185
+ });
186
+ } catch (error ) {
187
+ if (error instanceof Refused) return error.result;
188
+ throw error;
189
+ }
190
+ if (!marked.applied) {
191
+ if (marked.reason === 'unknown-run') return refused('unknown-run', runId);
192
+ return refused('run-ended', runId, { endState: marked.run?.end?.state ?? null });
193
+ }
194
+
195
+ // Step 3, outside every lock.
196
+ const cleanup = await endTree({ file, runId, run: marked.run, platform, sleep, clock, escalate });
197
+
198
+ // Step 4, after the tree is ended.
199
+ const attemptCancel = marked.run.childOf
200
+ ? { ...CHILD_SKIP }
201
+ : attemptCancelStep({ file, run: marked.run, by: String(by), reason: why, env, cwd, clock, cancelImpl });
202
+
203
+ // Step 5: always written.
204
+ const endState = source === 'timeout' ? 'timed-out' : 'cancelled';
205
+ const ended = transition(file, runId, ['cancelling'], (run ) => {
206
+ run.attemptCancel = attemptCancel;
207
+ run.cleanup = mergeCleanup(cleanup, run.supervisorCleanup);
208
+ run.end = { state: endState, at: clock().toISOString() };
209
+ run.phase = 'ended';
210
+ });
211
+ if (!ended.applied) return refused('run-ended', runId, { endState: ended.run?.end?.state ?? null, cleanup, attemptCancel });
212
+ return {
213
+ disposition: 'cancelled',
214
+ runId,
215
+ endState,
216
+ cleanup: ended.run.cleanup,
217
+ attemptCancel,
218
+ attemptCancelled: attemptCancel.disposition === 'cancelled',
219
+ };
220
+ }
221
+
222
+ module.exports = { cancelRun, mergeCleanup, CANCEL_TAKEOVER_MS, LAUNCH_WAIT_MS };