@khoavu882/doflow 2.0.0-beta.5 → 2.0.1

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 (494) hide show
  1. package/README.md +16 -16
  2. package/bin/doflow.js +22 -12
  3. package/core/.antigravity-plugin/plugin.json +1 -2
  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 +4 -1
  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-compact.sh +23 -27
  15. package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
  16. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  17. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  18. package/core/harnesses/claude/settings/settings.json +26 -9
  19. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  20. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  21. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  22. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  23. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  24. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  25. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  26. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  27. package/core/harnesses/codex/hooks/hooks.json +0 -10
  28. package/core/harnesses/codex/hooks/lib.sh +44 -25
  29. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
  30. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  31. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  32. package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
  33. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  34. package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
  35. package/core/harnesses/copilot/hooks/doflow-lib.sh +94 -0
  36. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  37. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  38. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  39. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  40. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  41. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  42. package/core/harnesses/copilot/hooks/doflow.json +51 -0
  43. package/core/harnesses/kiro/agents/doflow.json +1 -11
  44. package/core/harnesses/kiro/hooks/doflow.json +0 -10
  45. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  46. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +8 -3
  47. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +8 -3
  48. package/core/harnesses/kiro/hooks/session-start.sh +1 -0
  49. package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
  50. package/core/harnesses/pi/extensions/doflow-guard.js +11 -2
  51. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -2
  52. package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
  53. package/core/harnesses/shared/hooks/bridge/bridge.sh +126 -23
  54. package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
  55. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  56. package/core/harnesses/shared/hooks/bridge/respond.sh +16 -2
  57. package/core/harnesses/shared/hooks/policies/capture-failure.sh +49 -16
  58. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  59. package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
  60. package/core/harnesses/shared/hooks/policies/deny-json.sh +20 -7
  61. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  62. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  63. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  64. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  65. package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
  66. package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
  67. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +7 -8
  68. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +132 -57
  69. package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
  70. package/core/harnesses/shared/hooks/policies/stop-check.sh +27 -15
  71. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +6 -3
  72. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  73. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
  74. package/core/harnesses/shared/hooks/stream-hook-runner.js +58 -20
  75. package/core/harnesses/shared/locator/doflow-run +2 -3
  76. package/core/registry/advice.json +3 -3
  77. package/core/registry/assets.json +55 -82
  78. package/core/registry/capabilities.json +12 -0
  79. package/core/registry/contracts.json +58 -31
  80. package/core/registry/contribution-points.json +35 -0
  81. package/core/registry/gates.json +32 -0
  82. package/core/registry/guidance-caps.json +40 -0
  83. package/core/registry/harnesses.json +1445 -366
  84. package/core/registry/intake.json +53 -0
  85. package/core/registry/lifecycle.json +599 -996
  86. package/core/registry/mcp.json +5 -1
  87. package/core/registry/models.json +7 -1
  88. package/core/registry/package-templates/README.md +19 -0
  89. package/core/registry/package-templates/agent/files/agent.md +9 -0
  90. package/core/registry/package-templates/agent/template.json +4 -0
  91. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  92. package/core/registry/package-templates/capability-provider/template.json +11 -0
  93. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  94. package/core/registry/package-templates/debate-preset/template.json +4 -0
  95. package/core/registry/package-templates/intake-category/files/category.json +8 -0
  96. package/core/registry/package-templates/intake-category/template.json +4 -0
  97. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  98. package/core/registry/package-templates/review-preset/template.json +4 -0
  99. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  100. package/core/registry/package-templates/skill/template.json +4 -0
  101. package/core/registry/package-templates/worker/files/worker.json +92 -0
  102. package/core/registry/package-templates/worker/template.json +11 -0
  103. package/core/registry/packages/doflow/doflow-package.json +15 -0
  104. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  105. package/core/registry/protocols.json +61 -0
  106. package/core/registry/routes.json +17 -0
  107. package/core/registry/rules.json +431 -0
  108. package/core/registry/settings.json +272 -0
  109. package/core/registry/storage.json +687 -31
  110. package/core/registry/telemetry.json +236 -0
  111. package/core/registry/transcripts.json +142 -0
  112. package/core/registry/verification.json +76 -6
  113. package/core/registry/workflows.json +55 -0
  114. package/core/shared/agent-specs/core-implementer.md +3 -0
  115. package/core/shared/agent-specs/delivery-lead.md +68 -0
  116. package/core/shared/agent-specs/quality-guardian.md +5 -1
  117. package/core/shared/agent-specs/research-writer.md +10 -3
  118. package/core/shared/agent-specs/reviewer.md +53 -0
  119. package/core/shared/agent-specs/spec-analyst.md +5 -6
  120. package/core/shared/agent-specs/system-architect.md +5 -1
  121. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  122. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  123. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  124. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  125. package/core/shared/guidance/pointers/codex.md +1 -4
  126. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  127. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  128. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  129. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  130. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  131. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  132. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  133. package/core/shared/guidance/references/ORCHESTRATION.md +3 -2
  134. package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
  135. package/core/shared/guidance/rules/RULES.md +45 -0
  136. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  137. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  138. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  139. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  140. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  141. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  142. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  143. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  144. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  145. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  146. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  147. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +49 -5
  148. package/core/shared/scripts/doflow/bin/doflow-run +152 -34
  149. package/core/shared/skills/do/SKILL.md +14 -12
  150. package/core/shared/skills/do/references/advice_calls.md +26 -0
  151. package/core/shared/skills/do/references/pm_routing.md +1 -2
  152. package/core/shared/skills/do/references/program_mode.md +31 -0
  153. package/core/shared/skills/do-brainstorm/SKILL.md +45 -97
  154. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +36 -0
  155. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  156. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  157. package/core/shared/skills/do-code-review/SKILL.md +48 -148
  158. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  159. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  160. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  161. package/core/shared/skills/do-design/SKILL.md +23 -20
  162. package/core/shared/skills/do-diagnose/SKILL.md +18 -6
  163. package/core/shared/skills/do-document/SKILL.md +12 -1
  164. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  165. package/core/shared/skills/do-document/references/embedded-research.md +19 -7
  166. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  167. package/core/shared/skills/do-execute-plan/SKILL.md +19 -60
  168. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  169. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  170. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
  171. package/core/shared/skills/do-execute-plan/references/run_position.md +16 -2
  172. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  173. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  174. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  175. package/core/shared/skills/do-flow/SKILL.md +10 -11
  176. package/core/shared/skills/do-flow/references/task_classes.md +5 -5
  177. package/core/shared/skills/do-git/SKILL.md +4 -0
  178. package/core/shared/skills/do-implement/SKILL.md +13 -14
  179. package/core/shared/skills/do-plan/SKILL.md +34 -20
  180. package/core/shared/skills/do-test/SKILL.md +7 -4
  181. package/core/shared/templates/doflow/constitution-template.md +9 -3
  182. package/core/shared/templates/doflow/data-model-template.md +1 -1
  183. package/core/shared/templates/doflow/design-template.md +113 -16
  184. package/core/shared/templates/doflow/plan-template.md +11 -10
  185. package/core/shared/templates/doflow/question-log-template.md +14 -2
  186. package/core/shared/templates/doflow/requirement-template.md +1 -1
  187. package/core/shared/workflows/do-flow-chain.md +2 -0
  188. package/core/worker-evidence/antigravity/checks.json +14 -0
  189. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  190. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  191. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  192. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  193. package/core/worker-evidence/codex/checks.json +38 -0
  194. package/package.json +10 -6
  195. package/src/adapters/antigravity/index.js +143 -66
  196. package/src/adapters/claude/index.js +46 -5
  197. package/src/adapters/codex/agents.js +25 -15
  198. package/src/adapters/codex/hooks.js +39 -3
  199. package/src/adapters/codex/index.js +61 -13
  200. package/src/adapters/codex/native-projection.js +6 -2
  201. package/src/adapters/copilot/index.js +58 -23
  202. package/src/adapters/copy-tree.js +291 -46
  203. package/src/adapters/guidance-manifest.js +71 -0
  204. package/src/adapters/guidance-pointer.js +19 -0
  205. package/src/adapters/hook-commands.js +6 -10
  206. package/src/adapters/index.js +1 -0
  207. package/src/adapters/kiro/index.js +23 -12
  208. package/src/adapters/opencode/index.js +8 -8
  209. package/src/adapters/pi/hooks.js +2 -1
  210. package/src/adapters/pi/index.js +14 -7
  211. package/src/adapters/resolve-source.js +26 -2
  212. package/src/cli/commands/install.js +2 -1
  213. package/src/cli/commands/reconcile.js +53 -9
  214. package/src/cli/commands/remove.js +2 -2
  215. package/src/cli/commands/rollback.js +6 -3
  216. package/src/cli/commands/self-update.js +18 -9
  217. package/src/cli/commands/status.js +22 -3
  218. package/src/cli/commands/ui.js +99 -0
  219. package/src/cli/commands/update.js +2 -1
  220. package/src/cli/index.js +337 -32
  221. package/src/cli/runtime-commands.js +220 -60
  222. package/src/cli/shared.js +0 -2
  223. package/src/helper/git.js +1 -1
  224. package/src/helper/guardrails.js +1 -1
  225. package/src/helper/marker-merge.js +31 -13
  226. package/src/helper/package-ids.js +37 -0
  227. package/src/helper/semver.js +94 -0
  228. package/src/install/backup.js +105 -15
  229. package/src/install/context.js +1 -1
  230. package/src/install/targets.js +31 -16
  231. package/src/lifecycle/coverage.js +4 -16
  232. package/src/lifecycle/hook-tier.js +160 -0
  233. package/src/lifecycle/index.js +412 -78
  234. package/src/lifecycle/retired-harnesses.js +318 -0
  235. package/src/lifecycle/view.js +35 -15
  236. package/src/registry/index.js +551 -26
  237. package/src/registry/worker-ids.js +45 -0
  238. package/src/release/build-package.js +0 -3
  239. package/src/release/docs-blocks.js +472 -0
  240. package/src/release/preflight.js +425 -0
  241. package/src/release/rehearse.js +401 -0
  242. package/src/release/release-workflow.js +733 -0
  243. package/src/release/sync-plugin-versions.js +43 -7
  244. package/src/runtime/advice/bench-gate.js +25 -0
  245. package/src/runtime/advice/cli.js +59 -16
  246. package/src/runtime/advice/client.js +32 -9
  247. package/src/runtime/advice/config.js +98 -133
  248. package/src/runtime/advice/doctor.js +1 -1
  249. package/src/runtime/advice/elevation.js +97 -0
  250. package/src/runtime/advice/endpoint-guard.js +9 -3
  251. package/src/runtime/advice/mode.js +37 -0
  252. package/src/runtime/advice/record.js +18 -5
  253. package/src/runtime/advice/registry.js +4 -1
  254. package/src/runtime/advice/shadow-config.js +229 -0
  255. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  256. package/src/runtime/brief-rules.js +81 -0
  257. package/src/runtime/brief-tools.js +111 -34
  258. package/src/runtime/candidate.js +20 -11
  259. package/src/runtime/capabilities/executor.js +450 -0
  260. package/src/runtime/capabilities/loopback.js +41 -0
  261. package/src/runtime/capabilities/sanitizer.js +88 -0
  262. package/src/runtime/capability-router.js +135 -3
  263. package/src/runtime/checkouts.js +73 -11
  264. package/src/runtime/claims.js +13 -7
  265. package/src/runtime/cli.js +196 -19
  266. package/src/runtime/config-home.js +42 -0
  267. package/src/runtime/context/arbitrate.js +767 -0
  268. package/src/runtime/context/delivery-store.js +4 -6
  269. package/src/runtime/context/emission-rules.js +118 -0
  270. package/src/runtime/context/identity.js +227 -0
  271. package/src/runtime/context/pointer.js +67 -12
  272. package/src/runtime/context/primary-command.js +117 -0
  273. package/src/runtime/context/primary.js +344 -0
  274. package/src/runtime/context/summary-store.js +655 -0
  275. package/src/runtime/context-pack.js +158 -79
  276. package/src/runtime/decision-register.js +13 -4
  277. package/src/runtime/dispatch/admission.js +392 -0
  278. package/src/runtime/dispatch/attempt-end.js +274 -0
  279. package/src/runtime/dispatch/command.js +628 -0
  280. package/src/runtime/dispatch/controls.js +904 -0
  281. package/src/runtime/dispatch/envelope.js +276 -0
  282. package/src/runtime/dispatch/grants.js +310 -0
  283. package/src/runtime/dispatch/integrate.js +617 -0
  284. package/src/runtime/dispatch/loop.js +1835 -0
  285. package/src/runtime/dispatch/store.js +352 -0
  286. package/src/runtime/dispatch/verify-run.js +201 -0
  287. package/src/runtime/evidence-ledger.js +52 -7
  288. package/src/runtime/failure/capture.js +3 -1
  289. package/src/runtime/failure/cli.js +25 -9
  290. package/src/runtime/failure/home.js +57 -30
  291. package/src/runtime/failure/store.js +7 -2
  292. package/src/runtime/feature-pointer.js +174 -0
  293. package/src/runtime/feature-resolve.js +13 -2
  294. package/src/runtime/fleet/command.js +77 -0
  295. package/src/runtime/fleet/projects.js +178 -0
  296. package/src/runtime/fleet/tree.js +383 -0
  297. package/src/runtime/freshness.js +33 -3
  298. package/src/runtime/git-memo.js +46 -0
  299. package/src/runtime/guidance/ablation.js +427 -0
  300. package/src/runtime/health.js +624 -18
  301. package/src/runtime/history-compactor.js +13 -3
  302. package/src/runtime/implementation-gate.js +281 -26
  303. package/src/runtime/intake/engine.js +677 -0
  304. package/src/runtime/intake/index.js +136 -0
  305. package/src/runtime/intake/manifest.js +140 -0
  306. package/src/runtime/intake/taxonomy.js +206 -0
  307. package/src/runtime/inventory/coverage.js +1 -1
  308. package/src/runtime/inventory/index.js +1 -1
  309. package/src/runtime/inventory/precedence.js +2 -2
  310. package/src/runtime/inventory/siblings.js +6 -4
  311. package/src/runtime/knowledge/code-corpus.js +277 -0
  312. package/src/runtime/knowledge/code-lexical.js +130 -0
  313. package/src/runtime/knowledge/code-retrieval.js +307 -0
  314. package/src/runtime/knowledge/code-validation.js +106 -0
  315. package/src/runtime/knowledge/gp3-gate.js +74 -0
  316. package/src/runtime/knowledge/graph-adapter.js +288 -0
  317. package/src/runtime/knowledge/graph-fallback.js +394 -0
  318. package/src/runtime/knowledge/hybrid.js +136 -8
  319. package/src/runtime/knowledge/retrieval.js +34 -1
  320. package/src/runtime/knowledge/rtk-compress.js +77 -0
  321. package/src/runtime/knowledge/semble-bridge.py +106 -0
  322. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  323. package/src/runtime/knowledge/semble-provider.js +365 -0
  324. package/src/runtime/lifecycle/cli.js +88 -10
  325. package/src/runtime/lifecycle/event-store.js +8 -6
  326. package/src/runtime/lifecycle/followup.js +38 -7
  327. package/src/runtime/lifecycle/overview.js +26 -2
  328. package/src/runtime/lifecycle/report-store.js +8 -10
  329. package/src/runtime/lifecycle/retention.js +95 -31
  330. package/src/runtime/lifecycle/root.js +9 -1
  331. package/src/runtime/lifecycle/status.js +34 -17
  332. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  333. package/src/runtime/mask.js +134 -7
  334. package/src/runtime/mcp-consultations.js +40 -6
  335. package/src/runtime/model-router.js +79 -7
  336. package/src/runtime/models/catalogue.js +191 -0
  337. package/src/runtime/models/command.js +153 -0
  338. package/src/runtime/off-words.js +12 -0
  339. package/src/runtime/outcome.js +2 -2
  340. package/src/runtime/packages/command.js +1009 -0
  341. package/src/runtime/packages/contributions.js +116 -0
  342. package/src/runtime/packages/digest.js +132 -0
  343. package/src/runtime/packages/fetch.js +182 -0
  344. package/src/runtime/packages/grants.js +192 -0
  345. package/src/runtime/packages/index-file.js +134 -0
  346. package/src/runtime/packages/instruction.js +90 -0
  347. package/src/runtime/packages/manifest.js +331 -0
  348. package/src/runtime/packages/points.js +487 -0
  349. package/src/runtime/packages/programs.js +216 -0
  350. package/src/runtime/packages/scaffold.js +277 -0
  351. package/src/runtime/packages/source.js +90 -0
  352. package/src/runtime/packages/state.js +164 -0
  353. package/src/runtime/packages/store.js +449 -0
  354. package/src/runtime/packages/tar.js +106 -0
  355. package/src/runtime/packages/view.js +406 -0
  356. package/src/runtime/plan-check.js +59 -0
  357. package/src/runtime/plan-content.js +324 -0
  358. package/src/runtime/policy-init.js +118 -0
  359. package/src/runtime/profiles/doctor.js +96 -0
  360. package/src/runtime/profiles/resolve.js +244 -0
  361. package/src/runtime/profiles/rollback.js +254 -0
  362. package/src/runtime/profiles/sampler.js +41 -0
  363. package/src/runtime/readiness-record.js +16 -11
  364. package/src/runtime/readiness.js +46 -5
  365. package/src/runtime/research-request.js +50 -25
  366. package/src/runtime/retrieval-plan.js +10 -30
  367. package/src/runtime/retrieval-record.js +75 -5
  368. package/src/runtime/review/command.js +246 -0
  369. package/src/runtime/review/dominance.js +68 -0
  370. package/src/runtime/review/findings.js +434 -0
  371. package/src/runtime/review/identity.js +255 -0
  372. package/src/runtime/review/protocol-command.js +109 -0
  373. package/src/runtime/review/protocols.js +405 -0
  374. package/src/runtime/review/resolve-protocol.js +302 -0
  375. package/src/runtime/review/start.js +530 -0
  376. package/src/runtime/roadmap/attendance.js +137 -0
  377. package/src/runtime/roadmap/facts.js +479 -0
  378. package/src/runtime/roadmap/gates.js +273 -0
  379. package/src/runtime/roadmap/loop.js +1719 -0
  380. package/src/runtime/roadmap/merge.js +390 -0
  381. package/src/runtime/roadmap/rows.js +1122 -0
  382. package/src/runtime/roadmap/seed.js +1063 -0
  383. package/src/runtime/roadmap/worktree-cleanup.js +147 -0
  384. package/src/runtime/rules-report.js +336 -0
  385. package/src/runtime/scaffold/artifacts.js +60 -9
  386. package/src/runtime/scaffold/generate.js +21 -9
  387. package/src/runtime/settings/autopilot.js +106 -0
  388. package/src/runtime/settings/cli.js +280 -0
  389. package/src/runtime/settings/config-write.js +314 -0
  390. package/src/runtime/settings/files.js +271 -0
  391. package/src/runtime/settings/legacy.js +154 -0
  392. package/src/runtime/settings/lexical.js +56 -0
  393. package/src/runtime/settings/prepared-run.js +269 -0
  394. package/src/runtime/settings/project-layers.js +118 -0
  395. package/src/runtime/settings/refs.js +288 -0
  396. package/src/runtime/settings/registry.js +553 -0
  397. package/src/runtime/settings/resolve.js +713 -0
  398. package/src/runtime/settings/shared.js +37 -0
  399. package/src/runtime/settings/worker-billing.js +107 -0
  400. package/src/runtime/settings/write.js +269 -0
  401. package/src/runtime/slot-command.js +78 -0
  402. package/src/runtime/slots.js +671 -0
  403. package/src/runtime/source-version.js +1 -0
  404. package/src/runtime/state-root.js +217 -0
  405. package/src/runtime/storage/cli.js +12 -5
  406. package/src/runtime/storage/engine.js +9 -2
  407. package/src/runtime/storage/registry.js +14 -1
  408. package/src/runtime/storage/roots.js +66 -16
  409. package/src/runtime/storage/rules.js +82 -10
  410. package/src/runtime/storage/upkeep.js +10 -2
  411. package/src/runtime/storage/work-status.js +245 -26
  412. package/src/runtime/task-attempts.js +589 -69
  413. package/src/runtime/task-classifier.js +85 -14
  414. package/src/runtime/task-graph.js +373 -0
  415. package/src/runtime/task-run.js +778 -0
  416. package/src/runtime/task-scope.js +176 -55
  417. package/src/runtime/task-sources.js +147 -0
  418. package/src/runtime/task-state.js +373 -64
  419. package/src/runtime/team/admission.js +301 -0
  420. package/src/runtime/team/envelope.js +324 -0
  421. package/src/runtime/team/review-guard.js +156 -0
  422. package/src/runtime/trace/analyze.js +427 -0
  423. package/src/runtime/trace/baseline.js +770 -0
  424. package/src/runtime/trace/ledger.js +63 -140
  425. package/src/runtime/trace/otlp-export.js +568 -0
  426. package/src/runtime/trace/otlp-map.js +481 -0
  427. package/src/runtime/trace/spans.js +745 -0
  428. package/src/runtime/trace/telemetry.js +225 -0
  429. package/src/runtime/trace/views.js +1 -0
  430. package/src/runtime/transcripts/command.js +98 -0
  431. package/src/runtime/transcripts/cursor.js +326 -0
  432. package/src/runtime/transcripts/read.js +126 -0
  433. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  434. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  435. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  436. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  437. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  438. package/src/runtime/transcripts/registry.js +161 -0
  439. package/src/runtime/transcripts/stats.js +161 -0
  440. package/src/runtime/verb-view.js +267 -0
  441. package/src/runtime/verification/check-key.js +628 -0
  442. package/src/runtime/verification/contract-runner.js +546 -11
  443. package/src/runtime/verification/engine.js +499 -54
  444. package/src/runtime/verification/impact.js +617 -0
  445. package/src/runtime/verification/inflight.js +205 -0
  446. package/src/runtime/verification/registry.js +176 -0
  447. package/src/runtime/verification/reuse.js +184 -0
  448. package/src/runtime/verification/telemetry-emit.js +254 -0
  449. package/src/runtime/verification/test-selection.js +67 -7
  450. package/src/runtime/worker/active.js +90 -0
  451. package/src/runtime/worker/cancel.js +222 -0
  452. package/src/runtime/worker/catalogue.js +122 -0
  453. package/src/runtime/worker/command.js +677 -0
  454. package/src/runtime/worker/evidence.js +288 -0
  455. package/src/runtime/worker/launch.js +256 -0
  456. package/src/runtime/worker/models.js +137 -0
  457. package/src/runtime/worker/parsers.js +218 -0
  458. package/src/runtime/worker/probes/index.js +706 -0
  459. package/src/runtime/worker/process-tree.js +415 -0
  460. package/src/runtime/worker/quota.js +43 -0
  461. package/src/runtime/worker/readiness.js +713 -0
  462. package/src/runtime/worker/run-store.js +166 -0
  463. package/src/runtime/worker/supervisor.js +541 -0
  464. package/src/runtime/worker/worktree-check.js +252 -0
  465. package/src/runtime/workflow-engine.js +5 -1
  466. package/src/runtime/workflow-orchestrator.js +610 -67
  467. package/src/runtime/worktree.js +149 -22
  468. package/src/state/index.js +1 -1
  469. package/src/state/lockfile.js +64 -10
  470. package/src/state/run-lock.js +1 -1
  471. package/types/node-commonjs.d.ts +106 -7
  472. package/core/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
  473. package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
  474. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  475. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  476. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
  477. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  478. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
  479. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  480. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
  481. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  482. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  483. package/core/harnesses/gemini/hooks/session-start.sh +0 -7
  484. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  485. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -24
  486. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -91
  487. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -378
  488. package/core/shared/guidance/FLAGS.md +0 -25
  489. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  490. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  491. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  492. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  493. package/src/adapters/gemini/hooks.js +0 -251
  494. package/src/adapters/gemini/index.js +0 -429
@@ -6,14 +6,15 @@ const fs = require('node:fs');
6
6
  const path = require('node:path');
7
7
  const featureResolve = require('./feature-resolve.js');
8
8
 
9
- // Which feature a task record belongs to (feature 045, IC-001/IC-002).
9
+ // Which feature a task record belongs to (feature 045 IC-001/IC-002, 115 IC-004).
10
10
  //
11
11
  // Task ids are short and repeat across features (`A.1` exists in every feature that has a Phase A),
12
- // and the stores key records by the bare id. A task in one feature therefore read the evidence,
13
- // claims and outcome another feature had written under the same id. A feature that has a decision
14
- // register now keeps its records in a subdirectory named for the feature; a feature without one,
15
- // and a task id that is the feature's own slug, keep the flat layout, so no existing record is
16
- // moved, renamed or rewritten (FR-007) and a namespaced read never falls back to the flat file.
12
+ // and the stores key records by the bare id. Every task record therefore lives under a subdirectory
13
+ // named for its feature whenever a feature slug resolves (`--slug`, else the branch's), with or
14
+ // without a decision register and wherever the feature folder is; a task id that is the feature's
15
+ // own slug, and a call with no feature at all, keep the flat layout. Records written flat before
16
+ // this rule stay readable in place: a namespaced read falls back to the flat file when it is this
17
+ // feature's or names no feature, and the first write lands in the namespace (D-3). Nothing is moved.
17
18
 
18
19
  const SAFE_SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
19
20
  const SLUG_RULE = 'use letters, digits, dot, underscore or dash, start with a letter or digit, and no ".."';
@@ -34,19 +35,29 @@ function invalidSlugRefusal(slug )
34
35
  return { error: 'invalid-slug', message: `slug "${slug}" is not a valid feature slug: ${SLUG_RULE}`, hint: SLUG_RULE };
35
36
  }
36
37
 
37
- /** (projectRoot, slug) -> { slug, hasRegister } | { reason }. One resolver spawn per key per process. */
38
- const featureCache = new Map ();
38
+ /** projectRoot -> the branch's feature slug or null. One resolver spawn per root per process. */
39
+ const featureCache = new Map ();
39
40
 
40
41
  /** The resolver's raw answer, including thrown failures, shared by every consumer in this process. */
41
42
  const resolvedFeatureCache = new Map ();
42
43
 
43
- /** A slug the CLI named explicitly (`--slug`), applied to every store call in this process that was
44
- * not given its own. The dispatcher sets it once so the verbs whose handlers build their ledger
45
- * internally (readiness, evidence) route the same way as the ones that take `slug` directly. */
46
- let defaultSlug = null;
44
+ /** The record slug of the CLI call in progress (115 C-1): `--slug`, else the feature of the checkout
45
+ * the verb runs in, computed once by the dispatcher (a function is called on first use, then kept)
46
+ * and used by every store the call touches. `undefined` means no dispatcher call is in progress, and
47
+ * a store resolves the branch of its own project root. */
48
+ let callSlug = undefined;
47
49
 
48
- function setDefaultSlug(slug ) {
49
- defaultSlug = typeof slug === 'string' && slug !== '' ? slug : null;
50
+ function setRecordSlug(slug ) {
51
+ callSlug = slug === undefined || typeof slug === 'function' ? slug : (isSafeSlug(slug) ? slug : null);
52
+ }
53
+
54
+ function currentCallSlug() {
55
+ if (typeof callSlug === 'function') {
56
+ let value = null;
57
+ try { value = callSlug(); } catch { /* a failed lookup routes nothing to a guessed feature */ }
58
+ callSlug = isSafeSlug(value) ? value : null;
59
+ }
60
+ return callSlug ;
50
61
  }
51
62
 
52
63
  function clearTaskScopeCache() {
@@ -68,6 +79,12 @@ function resolveFeature({ projectRoot, slug = null }
68
79
  try {
69
80
  const value = featureResolve.resolveActiveFeature({ projectRoot, slug: slug || null });
70
81
  resolvedFeatureCache.set(key, { value });
82
+ // The branch's answer is also the answer for its own slug named explicitly: one spawn serves both.
83
+ const named = !slug && !value?.error ? value?.paths?.feature_slug : null;
84
+ if (isSafeSlug(named)) {
85
+ const namedKey = `${path.resolve(projectRoot)}\0${named}`;
86
+ if (!resolvedFeatureCache.has(namedKey)) resolvedFeatureCache.set(namedKey, { value });
87
+ }
71
88
  return value;
72
89
  } catch (error) {
73
90
  resolvedFeatureCache.set(key, { error });
@@ -75,73 +92,120 @@ function resolveFeature({ projectRoot, slug = null }
75
92
  }
76
93
  }
77
94
 
78
- /** The resolver reports the git top level, which is the project root or one of its ancestors, and
79
- * the feature folders live directly under it — so no `agent-docs/doflow` on the way up means there
80
- * is nothing to resolve and the spawn is skipped. */
81
- function hasFeatureFolderAbove(projectRoot ) {
82
- let dir = path.resolve(projectRoot);
83
- for (;;) {
84
- try {
85
- if (fs.statSync(path.join(dir, 'agent-docs', 'doflow')).isDirectory()) return true;
86
- } catch { /* not here */ }
87
- const parent = path.dirname(dir);
88
- if (parent === dir) return false;
89
- dir = parent;
95
+ /** Whether a branch or a feature folder could name a feature here: a `.git` or an
96
+ * `agent-docs/doflow` above the root. With neither the resolver has nothing to read and is skipped. */
97
+ function mayNameFeature(projectRoot ) {
98
+ for (let dir = path.resolve(projectRoot); ; dir = path.dirname(dir)) {
99
+ if (fs.existsSync(path.join(dir, '.git')) || fs.existsSync(path.join(dir, 'agent-docs', 'doflow'))) return true;
100
+ if (path.dirname(dir) === dir) return false;
90
101
  }
91
102
  }
92
103
 
93
- function featureFor(projectRoot , slug ) {
94
- const key = `${path.resolve(projectRoot)}\0${slug || ''}`;
95
- if (featureCache.has(key)) return featureCache.get(key);
96
- let value = { reason: 'no-feature' };
104
+ /** The feature slug the resolver gives for a root's branch (or its one feature folder), else null. */
105
+ function branchSlugFor(projectRoot ) {
106
+ const key = path.resolve(projectRoot);
107
+ if (featureCache.has(key)) return featureCache.get(key) ;
108
+ let value = null;
97
109
  try {
98
- if (hasFeatureFolderAbove(projectRoot)) {
99
- const found = resolveFeature({ projectRoot, slug: slug || null });
100
- if (!found.error && found.paths && isSafeSlug(found.paths.feature_slug)) {
101
- value = { slug: found.paths.feature_slug, hasRegister: found.paths.has_decisions === true, exists: fs.existsSync(found.featureDir) };
102
- }
110
+ if (mayNameFeature(projectRoot)) {
111
+ const found = resolveFeature({ projectRoot });
112
+ if (!found.error && found.paths && isSafeSlug(found.paths.feature_slug)) value = found.paths.feature_slug;
103
113
  }
104
114
  } catch { /* a resolver failure is "no feature", never an exception */ }
105
115
  featureCache.set(key, value);
106
116
  return value;
107
117
  }
108
118
 
119
+ /**
120
+ * The feature a call's records are keyed by when no `--slug` is given (115 C-1), as
121
+ * `featureSlugFor` names it but through the cached resolver: a task id that names a feature folder in
122
+ * the checkout is its own feature, else the branch's.
123
+ * @param {{projectRoot: string, taskId?: string|null}} options
124
+ * @returns {string|null}
125
+ */
126
+ function callerFeatureSlug({ projectRoot, taskId = null } ) {
127
+ if (isSafeSlug(taskId)) {
128
+ try { if (fs.statSync(path.join(projectRoot, 'agent-docs', 'doflow', taskId )).isDirectory()) return taskId ; } catch { /* not a folder */ }
129
+ }
130
+ return branchSlugFor(projectRoot);
131
+ }
132
+
109
133
  /**
110
134
  * @param {Object} options
111
135
  * @param {string} options.projectRoot
112
136
  * @param {string} options.taskId
113
- * @param {string|null} [options.slug] explicit feature; otherwise the `--slug` default, then the branch
137
+ * @param {string|null} [options.slug] explicit feature; otherwise the call's record slug, then the branch
114
138
  * @returns {{namespace: string|null, slug: string|null, reason: string}}
115
- * `reason` is `namespaced` when a namespace applies, else `no-feature`, `no-register` or
116
- * `feature-level-id`.
139
+ * `reason` is `namespaced` when a namespace applies, else `no-feature` or `feature-level-id`.
117
140
  */
118
141
  function resolveTaskScope({ projectRoot, taskId, slug = null } ) {
119
- const feature = featureFor(projectRoot, slug || defaultSlug);
120
- if (feature.reason) return { namespace: null, slug: null, reason: feature.reason };
121
- if (!feature.hasRegister) return { namespace: null, slug: feature.slug, reason: 'no-register' };
122
- if (taskId === feature.slug) return { namespace: null, slug: feature.slug, reason: 'feature-level-id' };
123
- return { namespace: feature.slug, slug: feature.slug, reason: 'namespaced' };
142
+ const call = slug ? undefined : currentCallSlug();
143
+ const feature = slug ? (isSafeSlug(slug) ? slug : null) : call !== undefined ? call : branchSlugFor(projectRoot);
144
+ if (!feature) return { namespace: null, slug: null, reason: 'no-feature' };
145
+ if (taskId === feature) return { namespace: null, slug: feature, reason: 'feature-level-id' };
146
+ return { namespace: feature, slug: feature, reason: 'namespaced' };
147
+ }
148
+
149
+ /** The largest record file `readRegularFileText` reads; a bigger one is refused (a record is a few KiB). */
150
+ const MAX_RECORD_FILE_BYTES = 1024 * 1024;
151
+
152
+ /** A record file's text, or null when it must not be read: not a regular file (a link, a FIFO, a
153
+ * device, a folder), larger than `MAX_RECORD_FILE_BYTES`, or any read error. The lifecycle reader's
154
+ * pattern (event-store.ts `readEventFile`): `lstat`, then `O_NOFOLLOW|O_NONBLOCK` so neither a link
155
+ * nor a FIFO swapped in after the `lstat` is followed or blocks, then `fstat` on the descriptor
156
+ * before reading. The one read every run-file probe and reader shares (FU-16c2wz). */
157
+ function readRegularFileText(file , fsImpl = fs) {
158
+ const flags = fs.constants;
159
+ let fd ;
160
+ try {
161
+ if (!fsImpl.lstatSync(file).isFile()) return null;
162
+ fd = fsImpl.openSync(file, flags.O_RDONLY | (flags.O_NOFOLLOW || 0) | (flags.O_NONBLOCK || 0));
163
+ const st = fsImpl.fstatSync(fd);
164
+ if (!st.isFile() || st.size > MAX_RECORD_FILE_BYTES) return null;
165
+ // Read at most one byte past the cap, so a file that grew after the fstat is still refused.
166
+ const buffer = Buffer.alloc(MAX_RECORD_FILE_BYTES + 1);
167
+ let length = 0;
168
+ for (let n = -1; n !== 0 && length <= MAX_RECORD_FILE_BYTES; length += n) {
169
+ n = fsImpl.readSync(fd, buffer, length, buffer.length - length, null);
170
+ }
171
+ return length > MAX_RECORD_FILE_BYTES ? null : buffer.subarray(0, length).toString('utf8');
172
+ } catch {
173
+ return null;
174
+ } finally {
175
+ if (fd !== undefined) { try { fsImpl.closeSync(fd); } catch { /* the read is done */ } }
176
+ }
124
177
  }
125
178
 
126
179
  /**
127
- * Whether an explicit slug, already known to be well-formed, names no feature folder here. The
128
- * records then go to the shared task store, which is today's behaviour and is worth one line to the
129
- * caller because the slug they typed did nothing.
130
- * @param {Object} options
131
- * @param {string} options.projectRoot
132
- * @param {string} options.slug
180
+ * Whether a flat record may be read as `slug`'s (D-3): it names that feature (`featureSlug` on a
181
+ * run, `slug` on a readiness record) or names none. A file that does not parse names nobody, so a
182
+ * namespaced read never falls back to it. The probe never blocks and follows no link: a flat file
183
+ * that is not a regular file of at most `MAX_RECORD_FILE_BYTES` is not read; `unsafeIsOurs` is then
184
+ * the answer, so a caller whose reader refuses such a file (the run store's `readRunRecord`) is
185
+ * pointed at it and reports it, and any other caller treats it as not ours.
186
+ * @param {string} file
187
+ * @param {string|null} slug
188
+ * @param {Object} [fsImpl]
189
+ * @param {boolean} [unsafeIsOurs]
133
190
  * @returns {boolean}
134
191
  */
135
- function slugNamesNoFeature({ projectRoot, slug } ) {
136
- const feature = featureFor(projectRoot, slug);
137
- return Boolean(feature.reason) || feature.exists === false;
192
+ function flatRecordIsOurs(file , slug , fsImpl = fs, unsafeIsOurs = false) {
193
+ let data ;
194
+ try {
195
+ const text = typeof fsImpl.lstatSync === 'function' && typeof fsImpl.openSync === 'function'
196
+ ? readRegularFileText(file, fsImpl) : fsImpl.readFileSync(file, 'utf8');
197
+ if (text === null) return unsafeIsOurs;
198
+ data = JSON.parse(text);
199
+ } catch { return false; }
200
+ const named = [data?.featureSlug, data?.slug].find((value) => typeof value === 'string' && value !== '');
201
+ return named === undefined || named === slug;
138
202
  }
139
203
 
140
204
  /**
141
- * The directory a task's record file for one store lives in (IC-002).
205
+ * The directory a task's record file for one store is written in (IC-002, 115 IC-004).
142
206
  * @param {Object} options
143
207
  * @param {string} options.projectRoot
144
- * @param {string} options.store state subdirectory: evidence | research | outcome | retrieval
208
+ * @param {string} options.store state subdirectory: evidence | research | outcome | retrieval | ...
145
209
  * @param {string} options.taskId
146
210
  * @param {string|null} [options.slug]
147
211
  * @returns {string}
@@ -152,4 +216,61 @@ function taskStoreDir({ projectRoot, store, taskId, slug = null }
152
216
  return namespace ? path.join(base, namespace) : base;
153
217
  }
154
218
 
155
- module.exports = { resolveFeature, resolveTaskScope, taskStoreDir, setDefaultSlug, clearTaskScopeCache, isSafeSlug, invalidSlugRefusal, slugNamesNoFeature };
219
+ /**
220
+ * One task record (115 C-1, D-3): `file`, where it is written (always the namespace when one
221
+ * applies); `read`, where it is read from (that file when it exists, else a flat file of the same
222
+ * name that `flatRecordIsOurs`); and `seed`, a `prepare` for the write lock that starts a namespaced
223
+ * file not written yet from that flat one, so a store that rebuilds from disk keeps what it read.
224
+ * @param {Object} options
225
+ * @param {string} options.projectRoot
226
+ * @param {string} options.store
227
+ * @param {string} options.taskId
228
+ * @param {string|null} [options.slug]
229
+ * @param {string} [options.name] the file name (default `<taskId>.json`)
230
+ * @param {Object} [options.fsImpl]
231
+ * @returns {{file: string, read: string, seed: () => void}}
232
+ */
233
+ function taskRecord({ projectRoot, store, taskId, slug = null, name = null, fsImpl = fs } ) {
234
+ const scope = resolveTaskScope({ projectRoot, taskId, slug });
235
+ return recordIn({ base: path.join(projectRoot, '.doflow', 'state', store), namespace: scope.namespace, name: name || `${taskId}.json`, fsImpl });
236
+ }
237
+
238
+ /**
239
+ * `taskRecord` for a store directory and a namespace already known (the orchestrator's run journal).
240
+ * @param {{base: string, namespace: string|null, name: string, fsImpl?: Object, refuseUnsafeFlat?: boolean}} options
241
+ * `refuseUnsafeFlat`: a flat file that is not a safely readable regular file is `read` (so the
242
+ * caller's safe reader refuses it by name) instead of being skipped
243
+ * @returns {{file: string, read: string, seed: () => void}}
244
+ */
245
+ function recordIn({ base, namespace, name, fsImpl = fs, refuseUnsafeFlat = false } ) {
246
+ const file = path.join(base, ...(namespace ? [namespace] : []), name);
247
+ const flat = path.join(base, name);
248
+ const fallback = () => (namespace && !fsImpl.existsSync(file) && fsImpl.existsSync(flat)
249
+ && flatRecordIsOurs(flat, namespace, fsImpl, refuseUnsafeFlat) ? flat : null);
250
+ const seed = () => {
251
+ const from = fallback();
252
+ // Required on use: the record mover (a declared writer of every task store) loads the storage registry.
253
+ if (from) require('./state-root.js').placeRecord(file, fsImpl.readFileSync(from), fsImpl);
254
+ };
255
+ return { file, read: fallback() || file, seed };
256
+ }
257
+
258
+ /**
259
+ * Whether an explicit, well-formed slug names no feature folder in any of the given checkouts. The
260
+ * records still go under that slug; a typo would start a namespace nobody reads, which is worth one
261
+ * line to the caller.
262
+ * @param {Object} options
263
+ * @param {Array<string>} options.projectRoots
264
+ * @param {string} options.slug
265
+ * @returns {boolean}
266
+ */
267
+ function slugNamesNoFeature({ projectRoots, slug } ) {
268
+ return !projectRoots.some((projectRoot) => {
269
+ try {
270
+ const found = resolveFeature({ projectRoot, slug });
271
+ return !found.error && fs.existsSync(found.featureDir);
272
+ } catch { return false; }
273
+ });
274
+ }
275
+
276
+ module.exports = { readRegularFileText, MAX_RECORD_FILE_BYTES, resolveFeature, resolveTaskScope, taskStoreDir, taskRecord, recordIn, flatRecordIsOurs, setRecordSlug, callerFeatureSlug, clearTaskScopeCache, isSafeSlug, invalidSlugRefusal, slugNamesNoFeature };
@@ -0,0 +1,147 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ // Task source records (feature 080, design C5, IC-013): what a task's declared `files:` looked like
6
+ // at claim, and how that compares with what they look like at resume.
7
+ //
8
+ // `measureSources` is a probe: it reads the filesystem, so the caller runs it before the ledger
9
+ // lock (claim before `mutate()`, resume before any lock). It starts no process: each file is hashed
10
+ // by 076's `sourceVersionFor` with a null tree state, which never runs git. `compareSources` is pure.
11
+
12
+ const fs = require('node:fs');
13
+ const path = require('node:path');
14
+ const { sourceVersionFor } = require('./source-version.js');
15
+
16
+ /** IC-013: a file larger than this is `unhashable`, `too-large`, and is never read. */
17
+ const MAX_HASH_BYTES = 1024 * 1024;
18
+ /** IC-013: only the first this-many `files:` entries are measured; more sets `sourcesTruncated`. */
19
+ const MAX_SOURCES = 64;
20
+
21
+ /** IC-013: `*`, `?`, whitespace or `(` anywhere, or a trailing `:<n>` / `:<n>-<n>` line annotation. */
22
+ const PATTERN = /[*?\s(]|:\d+(?:-\d+)?$/;
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ /** The entry as resolved on disk: trimmed, leading `./` dropped (the IC-016 normalisation). */
47
+ function normalizeEntry(file ) {
48
+ return String(file).trim().replace(/^(\.\/)+/, '');
49
+ }
50
+
51
+ function exists(dir ) {
52
+ if (!dir) return false;
53
+ try {
54
+ return fs.statSync(dir).isDirectory();
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+
60
+ /** One `lstat` per candidate root, in order; the first root holding the entry wins. */
61
+ function locate(entry , roots ) {
62
+ for (const root of roots) {
63
+ try {
64
+ return { root, stat: fs.lstatSync(path.resolve(root, entry)) };
65
+ } catch {
66
+ // Not under this root: try the next.
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+
72
+ function measureOne(declared , roots ) {
73
+ const file = String(declared);
74
+ const entry = normalizeEntry(file);
75
+ if (PATTERN.test(entry)) return { file, kind: 'unverifiable', fileHash: null, why: 'pattern' };
76
+
77
+ const found = locate(entry, roots);
78
+ if (!found) return { file, kind: 'absent', fileHash: null };
79
+ const { root, stat } = found;
80
+ if (stat.isSymbolicLink()) return { file, kind: 'unhashable', fileHash: null, why: 'link' };
81
+ if (stat.isDirectory()) return { file, kind: 'unhashable', fileHash: null, why: 'directory' };
82
+ if (!stat.isFile()) return { file, kind: 'unhashable', fileHash: null, why: 'read-error' };
83
+ // The size check precedes any read: an oversized file is never opened.
84
+ if (stat.size > MAX_HASH_BYTES) return { file, kind: 'unhashable', fileHash: null, why: 'too-large' };
85
+
86
+ const fileHash = sourceVersionFor(root, { file: entry }, null).fileHash;
87
+ if (!fileHash) return { file, kind: 'unhashable', fileHash: null, why: 'read-error' };
88
+ return { file, kind: 'file', fileHash };
89
+ }
90
+
91
+ /**
92
+ * IC-013: measures the task's first 64 `files:` entries, each resolved under `execWorktree` alone
93
+ * when one is given and exists (an entry absent there is `absent`), else under the `roots` (the
94
+ * `loadPlan` roots) in order where it exists. A missing `execWorktree` is reported as
95
+ * `execWorktreeMissing` and the roots are still measured.
96
+ */
97
+ function measureSources(task , execWorktree , roots = []) {
98
+ const declared = Array.isArray(task?.files) ? task.files : [];
99
+ const execWorktreeMissing = Boolean(execWorktree) && !exists(execWorktree);
100
+ // An execution worktree that exists is the only place an entry is read: a file absent there is
101
+ // `absent` (a removal), never the main copy. Without one, or when it is gone, the plan roots serve.
102
+ const candidates = execWorktree && !execWorktreeMissing
103
+ ? [path.resolve(execWorktree)]
104
+ : [...new Set(roots.map((root) => path.resolve(root)))];
105
+ const sources = declared.slice(0, MAX_SOURCES).map((file) => measureOne(file, candidates));
106
+ return {
107
+ sources,
108
+ sourcesTruncated: declared.length > MAX_SOURCES,
109
+ sourcesUnverifiable: sources.filter(opaque).map((s) => s.file),
110
+ execWorktreeMissing,
111
+ };
112
+ }
113
+
114
+ function opaque(s ) {
115
+ return s.kind === 'unhashable' || s.kind === 'unverifiable';
116
+ }
117
+
118
+ function changeOf(before , after ) {
119
+ if (before.kind === 'absent') return after.kind === 'absent' ? 'unchanged' : 'created';
120
+ if (after.kind === 'absent') return 'removed';
121
+ return before.fileHash === after.fileHash ? 'unchanged' : 'changed';
122
+ }
123
+
124
+ /**
125
+ * IC-013: compares an attempt's recorded `sources` with a fresh measurement of the same entries.
126
+ * The whole task is `unverifiable` when the attempt has no `sources`, was truncated, the execution
127
+ * worktree is gone, a recorded entry is missing from `after`, or any entry is `unhashable` or
128
+ * `unverifiable` at either time.
129
+ */
130
+ function compareSources(
131
+ before ,
132
+ after ,
133
+ ) {
134
+ const unverifiable = { unverifiable: true, files: [] };
135
+ if (!before || !Array.isArray(before.sources) || before.sourcesTruncated) return unverifiable;
136
+ if (!after || after.execWorktreeMissing || after.sourcesTruncated) return unverifiable;
137
+ const now = new Map(after.sources.map((s) => [s.file, s]));
138
+ const files = [];
139
+ for (const was of before.sources) {
140
+ const is = now.get(was.file);
141
+ if (!is || opaque(was) || opaque(is)) return unverifiable;
142
+ files.push({ file: was.file, change: changeOf(was, is) });
143
+ }
144
+ return { unverifiable: false, files };
145
+ }
146
+
147
+ module.exports = { MAX_HASH_BYTES, MAX_SOURCES, measureSources, compareSources };