@khoavu882/doflow 2.0.0-beta.5 → 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 (471) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  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/permission-audit.sh +3 -0
  9. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  10. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  11. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  12. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  13. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  14. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  15. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  16. package/core/harnesses/claude/settings/settings.json +18 -0
  17. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  18. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  19. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  20. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  21. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  22. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  23. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  24. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  25. package/core/harnesses/codex/hooks/lib.sh +44 -25
  26. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  27. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  28. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  29. package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
  30. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  31. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  32. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  33. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  34. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  35. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  36. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  37. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  38. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  39. package/core/harnesses/kiro/agents/doflow.json +1 -1
  40. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  41. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +1 -0
  42. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +1 -0
  43. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +1 -0
  44. package/core/harnesses/kiro/hooks/session-start.sh +1 -0
  45. package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
  46. package/core/harnesses/pi/extensions/doflow-guard.js +8 -0
  47. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +0 -1
  48. package/core/harnesses/shared/hooks/bridge/bridge.sh +102 -9
  49. package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
  50. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  51. package/core/harnesses/shared/hooks/bridge/respond.sh +10 -2
  52. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  53. package/core/harnesses/shared/hooks/policies/capture-failure.sh +48 -15
  54. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  55. package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
  56. package/core/harnesses/shared/hooks/policies/deny-json.sh +2 -11
  57. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  58. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  59. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  60. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  61. package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
  62. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +3 -2
  63. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +213 -12
  64. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +114 -38
  65. package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
  66. package/core/harnesses/shared/hooks/policies/stop-check.sh +20 -9
  67. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +5 -2
  68. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  69. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
  70. package/core/harnesses/shared/hooks/stream-hook-runner.js +47 -8
  71. package/core/harnesses/shared/locator/doflow-run +2 -3
  72. package/core/registry/advice.json +3 -3
  73. package/core/registry/assets.json +55 -82
  74. package/core/registry/capabilities.json +12 -0
  75. package/core/registry/contracts.json +58 -31
  76. package/core/registry/contribution-points.json +32 -0
  77. package/core/registry/gates.json +32 -0
  78. package/core/registry/guidance-caps.json +40 -0
  79. package/core/registry/harnesses.json +1442 -363
  80. package/core/registry/lifecycle.json +485 -304
  81. package/core/registry/mcp.json +5 -1
  82. package/core/registry/models.json +7 -1
  83. package/core/registry/package-templates/README.md +19 -0
  84. package/core/registry/package-templates/agent/files/agent.md +9 -0
  85. package/core/registry/package-templates/agent/template.json +4 -0
  86. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  87. package/core/registry/package-templates/capability-provider/template.json +11 -0
  88. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  89. package/core/registry/package-templates/debate-preset/template.json +4 -0
  90. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  91. package/core/registry/package-templates/review-preset/template.json +4 -0
  92. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  93. package/core/registry/package-templates/skill/template.json +4 -0
  94. package/core/registry/package-templates/worker/files/worker.json +92 -0
  95. package/core/registry/package-templates/worker/template.json +11 -0
  96. package/core/registry/packages/doflow/doflow-package.json +15 -0
  97. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  98. package/core/registry/protocols.json +61 -0
  99. package/core/registry/routes.json +17 -0
  100. package/core/registry/rules.json +431 -0
  101. package/core/registry/settings.json +276 -0
  102. package/core/registry/storage.json +679 -31
  103. package/core/registry/telemetry.json +236 -0
  104. package/core/registry/transcripts.json +142 -0
  105. package/core/registry/verification.json +76 -1
  106. package/core/registry/workflows.json +55 -0
  107. package/core/shared/agent-specs/core-implementer.md +3 -0
  108. package/core/shared/agent-specs/delivery-lead.md +68 -0
  109. package/core/shared/agent-specs/quality-guardian.md +5 -1
  110. package/core/shared/agent-specs/research-writer.md +10 -3
  111. package/core/shared/agent-specs/reviewer.md +53 -0
  112. package/core/shared/agent-specs/spec-analyst.md +5 -6
  113. package/core/shared/agent-specs/system-architect.md +5 -1
  114. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  115. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  116. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  117. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  118. package/core/shared/guidance/pointers/codex.md +1 -4
  119. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  120. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  121. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  122. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  123. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  124. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  125. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  126. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  127. package/core/shared/guidance/rules/RULES.md +45 -0
  128. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  129. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  130. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  131. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  132. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  133. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  134. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  135. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  136. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  137. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  138. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  139. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  140. package/core/shared/scripts/doflow/bin/doflow-run +149 -34
  141. package/core/shared/skills/do/SKILL.md +12 -10
  142. package/core/shared/skills/do/references/advice_calls.md +26 -0
  143. package/core/shared/skills/do/references/pm_routing.md +1 -2
  144. package/core/shared/skills/do/references/program_mode.md +31 -0
  145. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  146. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  147. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  148. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  149. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  150. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  151. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  152. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  153. package/core/shared/skills/do-design/SKILL.md +18 -17
  154. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  155. package/core/shared/skills/do-document/SKILL.md +11 -1
  156. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  157. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  158. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  159. package/core/shared/skills/do-execute-plan/SKILL.md +15 -58
  160. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  161. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  162. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
  163. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  164. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  165. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  166. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  167. package/core/shared/skills/do-flow/SKILL.md +8 -9
  168. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  169. package/core/shared/skills/do-git/SKILL.md +4 -0
  170. package/core/shared/skills/do-implement/SKILL.md +4 -7
  171. package/core/shared/skills/do-plan/SKILL.md +26 -15
  172. package/core/shared/skills/do-test/SKILL.md +7 -4
  173. package/core/shared/templates/doflow/constitution-template.md +9 -3
  174. package/core/shared/templates/doflow/data-model-template.md +1 -1
  175. package/core/shared/templates/doflow/design-template.md +113 -16
  176. package/core/shared/templates/doflow/plan-template.md +11 -10
  177. package/core/shared/templates/doflow/question-log-template.md +14 -2
  178. package/core/shared/templates/doflow/requirement-template.md +1 -1
  179. package/core/worker-evidence/antigravity/checks.json +14 -0
  180. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  181. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  182. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  183. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  184. package/core/worker-evidence/codex/checks.json +38 -0
  185. package/package.json +10 -6
  186. package/src/adapters/antigravity/index.js +143 -66
  187. package/src/adapters/claude/index.js +24 -4
  188. package/src/adapters/codex/agents.js +25 -15
  189. package/src/adapters/codex/index.js +48 -13
  190. package/src/adapters/codex/native-projection.js +6 -2
  191. package/src/adapters/copilot/index.js +58 -23
  192. package/src/adapters/copy-tree.js +290 -44
  193. package/src/adapters/guidance-manifest.js +71 -0
  194. package/src/adapters/guidance-pointer.js +19 -0
  195. package/src/adapters/hook-commands.js +6 -10
  196. package/src/adapters/index.js +1 -0
  197. package/src/adapters/kiro/index.js +23 -12
  198. package/src/adapters/opencode/index.js +8 -8
  199. package/src/adapters/pi/hooks.js +2 -1
  200. package/src/adapters/pi/index.js +14 -7
  201. package/src/adapters/resolve-source.js +26 -2
  202. package/src/cli/commands/install.js +2 -1
  203. package/src/cli/commands/reconcile.js +53 -9
  204. package/src/cli/commands/remove.js +2 -2
  205. package/src/cli/commands/rollback.js +6 -3
  206. package/src/cli/commands/self-update.js +18 -9
  207. package/src/cli/commands/status.js +21 -2
  208. package/src/cli/commands/ui.js +99 -0
  209. package/src/cli/commands/update.js +2 -1
  210. package/src/cli/index.js +322 -29
  211. package/src/cli/runtime-commands.js +209 -57
  212. package/src/cli/shared.js +0 -2
  213. package/src/helper/git.js +1 -1
  214. package/src/helper/guardrails.js +1 -1
  215. package/src/helper/marker-merge.js +31 -13
  216. package/src/helper/package-ids.js +37 -0
  217. package/src/helper/semver.js +94 -0
  218. package/src/install/backup.js +105 -15
  219. package/src/install/context.js +1 -1
  220. package/src/install/targets.js +31 -16
  221. package/src/lifecycle/coverage.js +20 -3
  222. package/src/lifecycle/hook-tier.js +160 -0
  223. package/src/lifecycle/index.js +412 -78
  224. package/src/lifecycle/retired-harnesses.js +318 -0
  225. package/src/lifecycle/view.js +35 -15
  226. package/src/registry/index.js +551 -26
  227. package/src/registry/worker-ids.js +45 -0
  228. package/src/release/build-package.js +0 -3
  229. package/src/release/docs-blocks.js +492 -0
  230. package/src/release/preflight.js +425 -0
  231. package/src/release/rehearse.js +401 -0
  232. package/src/release/release-workflow.js +733 -0
  233. package/src/release/sync-plugin-versions.js +43 -7
  234. package/src/runtime/advice/bench-gate.js +25 -0
  235. package/src/runtime/advice/cli.js +59 -16
  236. package/src/runtime/advice/client.js +32 -9
  237. package/src/runtime/advice/config.js +98 -133
  238. package/src/runtime/advice/doctor.js +1 -1
  239. package/src/runtime/advice/elevation.js +97 -0
  240. package/src/runtime/advice/endpoint-guard.js +9 -3
  241. package/src/runtime/advice/mode.js +37 -0
  242. package/src/runtime/advice/record.js +18 -5
  243. package/src/runtime/advice/registry.js +4 -1
  244. package/src/runtime/advice/shadow-config.js +229 -0
  245. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  246. package/src/runtime/brief-rules.js +81 -0
  247. package/src/runtime/brief-tools.js +111 -34
  248. package/src/runtime/candidate.js +20 -11
  249. package/src/runtime/capabilities/executor.js +450 -0
  250. package/src/runtime/capabilities/loopback.js +41 -0
  251. package/src/runtime/capabilities/sanitizer.js +88 -0
  252. package/src/runtime/capability-router.js +135 -3
  253. package/src/runtime/checkouts.js +73 -11
  254. package/src/runtime/claims.js +13 -7
  255. package/src/runtime/cli.js +196 -19
  256. package/src/runtime/config-home.js +42 -0
  257. package/src/runtime/context/arbitrate.js +767 -0
  258. package/src/runtime/context/delivery-store.js +4 -6
  259. package/src/runtime/context/emission-rules.js +118 -0
  260. package/src/runtime/context/identity.js +227 -0
  261. package/src/runtime/context/primary-command.js +117 -0
  262. package/src/runtime/context/primary.js +344 -0
  263. package/src/runtime/context/summary-store.js +655 -0
  264. package/src/runtime/context-pack.js +154 -78
  265. package/src/runtime/decision-register.js +13 -4
  266. package/src/runtime/dispatch/admission.js +392 -0
  267. package/src/runtime/dispatch/attempt-end.js +274 -0
  268. package/src/runtime/dispatch/command.js +628 -0
  269. package/src/runtime/dispatch/controls.js +904 -0
  270. package/src/runtime/dispatch/envelope.js +276 -0
  271. package/src/runtime/dispatch/grants.js +310 -0
  272. package/src/runtime/dispatch/integrate.js +617 -0
  273. package/src/runtime/dispatch/loop.js +1835 -0
  274. package/src/runtime/dispatch/store.js +352 -0
  275. package/src/runtime/dispatch/verify-run.js +201 -0
  276. package/src/runtime/evidence-ledger.js +34 -7
  277. package/src/runtime/failure/capture.js +3 -1
  278. package/src/runtime/failure/cli.js +25 -9
  279. package/src/runtime/failure/home.js +57 -30
  280. package/src/runtime/failure/store.js +7 -2
  281. package/src/runtime/feature-pointer.js +174 -0
  282. package/src/runtime/feature-resolve.js +13 -2
  283. package/src/runtime/fleet/command.js +77 -0
  284. package/src/runtime/fleet/projects.js +178 -0
  285. package/src/runtime/fleet/tree.js +383 -0
  286. package/src/runtime/freshness.js +33 -3
  287. package/src/runtime/git-memo.js +46 -0
  288. package/src/runtime/guidance/ablation.js +427 -0
  289. package/src/runtime/health.js +565 -17
  290. package/src/runtime/history-compactor.js +13 -3
  291. package/src/runtime/implementation-gate.js +277 -22
  292. package/src/runtime/inventory/coverage.js +1 -1
  293. package/src/runtime/inventory/index.js +1 -1
  294. package/src/runtime/inventory/precedence.js +2 -2
  295. package/src/runtime/inventory/siblings.js +6 -4
  296. package/src/runtime/knowledge/code-corpus.js +277 -0
  297. package/src/runtime/knowledge/code-lexical.js +130 -0
  298. package/src/runtime/knowledge/code-retrieval.js +307 -0
  299. package/src/runtime/knowledge/code-validation.js +106 -0
  300. package/src/runtime/knowledge/gp3-gate.js +74 -0
  301. package/src/runtime/knowledge/graph-adapter.js +288 -0
  302. package/src/runtime/knowledge/graph-fallback.js +394 -0
  303. package/src/runtime/knowledge/hybrid.js +136 -8
  304. package/src/runtime/knowledge/retrieval.js +34 -1
  305. package/src/runtime/knowledge/rtk-compress.js +77 -0
  306. package/src/runtime/knowledge/semble-bridge.py +106 -0
  307. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  308. package/src/runtime/knowledge/semble-provider.js +365 -0
  309. package/src/runtime/lifecycle/cli.js +16 -6
  310. package/src/runtime/lifecycle/event-store.js +8 -6
  311. package/src/runtime/lifecycle/followup.js +6 -1
  312. package/src/runtime/lifecycle/overview.js +24 -1
  313. package/src/runtime/lifecycle/report-store.js +8 -10
  314. package/src/runtime/lifecycle/retention.js +95 -31
  315. package/src/runtime/lifecycle/root.js +9 -1
  316. package/src/runtime/lifecycle/status.js +34 -17
  317. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  318. package/src/runtime/mask.js +134 -7
  319. package/src/runtime/mcp-consultations.js +40 -6
  320. package/src/runtime/model-router.js +79 -7
  321. package/src/runtime/models/catalogue.js +191 -0
  322. package/src/runtime/models/command.js +153 -0
  323. package/src/runtime/off-words.js +12 -0
  324. package/src/runtime/outcome.js +2 -2
  325. package/src/runtime/packages/command.js +1009 -0
  326. package/src/runtime/packages/contributions.js +116 -0
  327. package/src/runtime/packages/digest.js +132 -0
  328. package/src/runtime/packages/fetch.js +182 -0
  329. package/src/runtime/packages/grants.js +192 -0
  330. package/src/runtime/packages/index-file.js +134 -0
  331. package/src/runtime/packages/instruction.js +90 -0
  332. package/src/runtime/packages/manifest.js +331 -0
  333. package/src/runtime/packages/points.js +477 -0
  334. package/src/runtime/packages/programs.js +216 -0
  335. package/src/runtime/packages/scaffold.js +277 -0
  336. package/src/runtime/packages/source.js +90 -0
  337. package/src/runtime/packages/state.js +164 -0
  338. package/src/runtime/packages/store.js +449 -0
  339. package/src/runtime/packages/tar.js +106 -0
  340. package/src/runtime/packages/view.js +406 -0
  341. package/src/runtime/plan-check.js +59 -0
  342. package/src/runtime/plan-content.js +324 -0
  343. package/src/runtime/policy-init.js +118 -0
  344. package/src/runtime/profiles/doctor.js +96 -0
  345. package/src/runtime/profiles/resolve.js +244 -0
  346. package/src/runtime/profiles/rollback.js +254 -0
  347. package/src/runtime/profiles/sampler.js +41 -0
  348. package/src/runtime/readiness-record.js +16 -11
  349. package/src/runtime/readiness.js +46 -5
  350. package/src/runtime/research-request.js +50 -25
  351. package/src/runtime/retrieval-plan.js +10 -30
  352. package/src/runtime/retrieval-record.js +75 -5
  353. package/src/runtime/review/command.js +246 -0
  354. package/src/runtime/review/dominance.js +68 -0
  355. package/src/runtime/review/findings.js +434 -0
  356. package/src/runtime/review/identity.js +255 -0
  357. package/src/runtime/review/protocol-command.js +109 -0
  358. package/src/runtime/review/protocols.js +405 -0
  359. package/src/runtime/review/resolve-protocol.js +302 -0
  360. package/src/runtime/review/start.js +530 -0
  361. package/src/runtime/roadmap/facts.js +479 -0
  362. package/src/runtime/roadmap/gates.js +270 -0
  363. package/src/runtime/roadmap/loop.js +1636 -0
  364. package/src/runtime/roadmap/merge.js +381 -0
  365. package/src/runtime/roadmap/rows.js +1113 -0
  366. package/src/runtime/roadmap/seed.js +951 -0
  367. package/src/runtime/rules-report.js +336 -0
  368. package/src/runtime/scaffold/artifacts.js +60 -9
  369. package/src/runtime/scaffold/generate.js +21 -9
  370. package/src/runtime/settings/autopilot.js +106 -0
  371. package/src/runtime/settings/cli.js +280 -0
  372. package/src/runtime/settings/config-write.js +314 -0
  373. package/src/runtime/settings/files.js +271 -0
  374. package/src/runtime/settings/legacy.js +154 -0
  375. package/src/runtime/settings/lexical.js +56 -0
  376. package/src/runtime/settings/prepared-run.js +269 -0
  377. package/src/runtime/settings/project-layers.js +118 -0
  378. package/src/runtime/settings/refs.js +288 -0
  379. package/src/runtime/settings/registry.js +553 -0
  380. package/src/runtime/settings/resolve.js +713 -0
  381. package/src/runtime/settings/shared.js +37 -0
  382. package/src/runtime/settings/worker-billing.js +107 -0
  383. package/src/runtime/settings/write.js +269 -0
  384. package/src/runtime/slot-command.js +78 -0
  385. package/src/runtime/slots.js +671 -0
  386. package/src/runtime/source-version.js +1 -0
  387. package/src/runtime/state-root.js +217 -0
  388. package/src/runtime/storage/cli.js +12 -5
  389. package/src/runtime/storage/engine.js +9 -2
  390. package/src/runtime/storage/registry.js +14 -1
  391. package/src/runtime/storage/roots.js +66 -16
  392. package/src/runtime/storage/rules.js +82 -10
  393. package/src/runtime/storage/upkeep.js +10 -2
  394. package/src/runtime/storage/work-status.js +245 -26
  395. package/src/runtime/task-attempts.js +589 -69
  396. package/src/runtime/task-classifier.js +47 -11
  397. package/src/runtime/task-graph.js +373 -0
  398. package/src/runtime/task-run.js +778 -0
  399. package/src/runtime/task-scope.js +176 -55
  400. package/src/runtime/task-sources.js +147 -0
  401. package/src/runtime/task-state.js +374 -64
  402. package/src/runtime/team/admission.js +301 -0
  403. package/src/runtime/team/envelope.js +324 -0
  404. package/src/runtime/team/review-guard.js +156 -0
  405. package/src/runtime/trace/analyze.js +427 -0
  406. package/src/runtime/trace/baseline.js +770 -0
  407. package/src/runtime/trace/ledger.js +63 -140
  408. package/src/runtime/trace/otlp-export.js +568 -0
  409. package/src/runtime/trace/otlp-map.js +481 -0
  410. package/src/runtime/trace/spans.js +745 -0
  411. package/src/runtime/trace/telemetry.js +225 -0
  412. package/src/runtime/trace/views.js +1 -0
  413. package/src/runtime/transcripts/command.js +98 -0
  414. package/src/runtime/transcripts/cursor.js +326 -0
  415. package/src/runtime/transcripts/read.js +126 -0
  416. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  417. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  418. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  419. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  420. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  421. package/src/runtime/transcripts/registry.js +161 -0
  422. package/src/runtime/transcripts/stats.js +161 -0
  423. package/src/runtime/verification/check-key.js +628 -0
  424. package/src/runtime/verification/contract-runner.js +546 -11
  425. package/src/runtime/verification/engine.js +499 -9
  426. package/src/runtime/verification/impact.js +617 -0
  427. package/src/runtime/verification/inflight.js +205 -0
  428. package/src/runtime/verification/registry.js +176 -0
  429. package/src/runtime/verification/reuse.js +184 -0
  430. package/src/runtime/verification/telemetry-emit.js +254 -0
  431. package/src/runtime/verification/test-selection.js +67 -7
  432. package/src/runtime/worker/active.js +90 -0
  433. package/src/runtime/worker/cancel.js +222 -0
  434. package/src/runtime/worker/catalogue.js +122 -0
  435. package/src/runtime/worker/command.js +677 -0
  436. package/src/runtime/worker/evidence.js +288 -0
  437. package/src/runtime/worker/launch.js +288 -0
  438. package/src/runtime/worker/models.js +137 -0
  439. package/src/runtime/worker/parsers.js +218 -0
  440. package/src/runtime/worker/probes/index.js +706 -0
  441. package/src/runtime/worker/process-tree.js +415 -0
  442. package/src/runtime/worker/quota.js +43 -0
  443. package/src/runtime/worker/readiness.js +713 -0
  444. package/src/runtime/worker/run-store.js +166 -0
  445. package/src/runtime/worker/supervisor.js +541 -0
  446. package/src/runtime/worker/worktree-check.js +252 -0
  447. package/src/runtime/workflow-engine.js +5 -1
  448. package/src/runtime/workflow-orchestrator.js +578 -63
  449. package/src/runtime/worktree.js +149 -22
  450. package/src/state/index.js +1 -1
  451. package/src/state/lockfile.js +64 -10
  452. package/src/state/run-lock.js +1 -1
  453. package/types/node-commonjs.d.ts +106 -7
  454. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  455. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  456. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
  457. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  458. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
  459. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  460. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
  461. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  462. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  463. package/core/harnesses/gemini/hooks/session-start.sh +0 -7
  464. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  465. package/core/shared/guidance/FLAGS.md +0 -25
  466. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  467. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  468. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  469. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  470. package/src/adapters/gemini/hooks.js +0 -251
  471. package/src/adapters/gemini/index.js +0 -429
@@ -27,6 +27,7 @@ const fs = require('node:fs');
27
27
  const path = require('node:path');
28
28
  const crypto = require('node:crypto');
29
29
  const { execFileSync } = require('node:child_process');
30
+ const { memoGit } = require('./git-memo.js');
30
31
 
31
32
  /** Worktree ids become branch names and directory names, so anything outside this set is a
32
33
  * potential argument-injection or path-traversal vector. Rejecting loudly beats sanitising
@@ -56,30 +57,67 @@ function sha256File(file ) {
56
57
  return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
57
58
  }
58
59
 
59
- function git(args , cwd , { allowFailure = false } = {}) {
60
+ /** Bounds for git children (082 IC-016). Null fields leave execFileSync's defaults, which is the
61
+ * bench behaviour: no timeout and the parent's environment. */
62
+
63
+
64
+ /** What `removeVerified` observed after removing (082 IC-021). `errors` holds exit codes or error
65
+ * codes only, never git's output, so a record can carry it without leaking repository text. */
66
+
67
+
68
+
69
+
70
+
71
+
72
+ function git(
73
+ args ,
74
+ cwd ,
75
+ { allowFailure = false, timeoutMs = null, env = null } = {},
76
+ ) {
77
+ const opts = { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] };
78
+ if (timeoutMs != null) opts.timeout = timeoutMs;
79
+ if (env != null) opts.env = env;
60
80
  try {
61
- return {
62
- ok: true,
63
- stdout: execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }),
64
- };
81
+ return { ok: true, stdout: execFileSync('git', args, opts) };
65
82
  } catch (err ) {
66
83
  if (!allowFailure) throw err;
67
- return { ok: false, stdout: '', stderr: err.stderr ? String(err.stderr) : String(err.message) };
84
+ return {
85
+ ok: false,
86
+ stdout: '',
87
+ stderr: err.stderr ? String(err.stderr) : String(err.message),
88
+ // Exit status, else the signal of a timed-out child, else the spawn error code.
89
+ code: String(err.status ?? err.signal ?? err.code ?? 'unknown'),
90
+ };
68
91
  }
69
92
  }
70
93
 
71
94
  class WorktreeManager {
72
95
  repoRoot ;
73
96
  worktreesDir ;
97
+ branchPrefix ;
98
+ gitBounds ;
74
99
 
75
100
  /**
76
101
  * @param {string} repoRoot repository whose worktrees are managed
77
102
  * @param {string} [worktreesDir] override the sandbox root; defaults to `<repo>/.doflow/worktrees`,
78
103
  * which is gitignored so sandboxes never appear as untracked noise in the parent tree.
104
+ * @param {object} [options] 082 IC-016: `branchPrefix` (default `task/`), and `timeoutMs` and
105
+ * `env` passed to every git child when given. Omitted, every bench path is unchanged.
79
106
  */
80
- constructor(repoRoot , worktreesDir ) {
107
+ constructor(
108
+ repoRoot ,
109
+ worktreesDir ,
110
+ { branchPrefix = 'task/', timeoutMs = null, env = null } = {},
111
+ ) {
81
112
  this.repoRoot = repoRoot;
82
113
  this.worktreesDir = worktreesDir || path.join(repoRoot, '.doflow', 'worktrees');
114
+ this.branchPrefix = branchPrefix;
115
+ this.gitBounds = { timeoutMs, env };
116
+ }
117
+
118
+ /** The module's git helper with this manager's bounds applied. */
119
+ git(args , cwd , opts = {}) {
120
+ return git(args, cwd, { ...opts, ...this.gitBounds });
83
121
  }
84
122
 
85
123
  static assertSafeId(taskId ) {
@@ -95,13 +133,13 @@ class WorktreeManager {
95
133
 
96
134
  branchFor(taskId ) {
97
135
  WorktreeManager.assertSafeId(taskId);
98
- return `task/${taskId}`;
136
+ return `${this.branchPrefix}${taskId}`;
99
137
  }
100
138
 
101
139
  /** Whether git itself considers this path a registered worktree — the directory existing on disk
102
140
  * is not the same question, which is the bug fixed here. */
103
141
  isRegistered(wtPath ) {
104
- const res = git(['worktree', 'list', '--porcelain'], this.repoRoot, { allowFailure: true });
142
+ const res = this.git(['worktree', 'list', '--porcelain'], this.repoRoot, { allowFailure: true });
105
143
  if (!res.ok) return false;
106
144
  const resolved = path.resolve(wtPath);
107
145
  return res.stdout
@@ -110,6 +148,52 @@ class WorktreeManager {
110
148
  .some((l ) => path.resolve(l.slice('worktree '.length).trim()) === resolved);
111
149
  }
112
150
 
151
+ /**
152
+ * Drops git's administrative record for `wtPath` when its directory is gone, and no other. A
153
+ * blanket `git worktree prune` also forgets every unrelated worktree of the user's whose directory
154
+ * is merely missing (an unmounted drive, a directory deleted by hand), which is not this
155
+ * manager's to decide. Git has no per-path prune, so this reads `<common-dir>/worktrees/<name>/gitdir`
156
+ * (the file git itself reads to decide a record is prunable) and removes only the matching record.
157
+ * A locked record, or one whose `.git` file is still present, is left alone.
158
+ */
159
+ dropStaleRecord(wtPath ) {
160
+ // The repository's common directory does not move while one call runs; a failed read is not kept.
161
+ const routing = this.gitBounds.env ?? process.env;
162
+ const common = memoGit(`commonDir:${this.repoRoot}:${routing.GIT_DIR ?? ''}:${routing.GIT_COMMON_DIR ?? ''}`, () => {
163
+ const res = this.git(['rev-parse', '--git-common-dir'], this.repoRoot, { allowFailure: true });
164
+ return res.ok ? res.stdout.trim() : undefined;
165
+ });
166
+ if (common === undefined) return;
167
+ const admin = path.join(path.resolve(this.repoRoot, common), 'worktrees');
168
+ let names ;
169
+ try {
170
+ names = fs.readdirSync(admin);
171
+ } catch {
172
+ return;
173
+ }
174
+ // git writes the real path of the parent, so compare with that of the worktree's parent too.
175
+ const target = path.resolve(wtPath);
176
+ let realTarget = target;
177
+ try {
178
+ realTarget = path.join(fs.realpathSync(path.dirname(target)), path.basename(target));
179
+ } catch {
180
+ // The parent is gone as well: only the plain form can match.
181
+ }
182
+ for (const name of names) {
183
+ const record = path.join(admin, name);
184
+ try {
185
+ if (fs.existsSync(path.join(record, 'locked'))) continue;
186
+ // A relative gitdir (worktree.useRelativePaths) is relative to the record directory.
187
+ const dotGit = path.resolve(record, fs.readFileSync(path.join(record, 'gitdir'), 'utf8').trim());
188
+ if (path.dirname(dotGit) !== target && path.dirname(dotGit) !== realTarget) continue;
189
+ if (fs.existsSync(dotGit)) continue;
190
+ fs.rmSync(record, { recursive: true, force: true });
191
+ } catch {
192
+ // An unreadable record is not ours to judge.
193
+ }
194
+ }
195
+ }
196
+
113
197
  /**
114
198
  * Create an isolated worktree, reusing it only when git agrees it is registered. Returns the
115
199
  * absolute path.
@@ -120,23 +204,23 @@ class WorktreeManager {
120
204
 
121
205
  if (this.isRegistered(wtPath)) return wtPath;
122
206
 
123
- // A leftover directory that git does not know about blocks `worktree add`. Clear it, and
124
- // prune git's stale administrative records, before trying.
207
+ // A leftover directory that git does not know about blocks `worktree add`. Clear it, and this
208
+ // path's stale administrative record, before trying; other worktrees' records are not touched.
125
209
  if (fs.existsSync(wtPath)) fs.rmSync(wtPath, { recursive: true, force: true });
126
- git(['worktree', 'prune'], this.repoRoot, { allowFailure: true });
210
+ this.dropStaleRecord(wtPath);
127
211
 
128
212
  fs.mkdirSync(this.worktreesDir, { recursive: true });
129
213
 
130
- const branchExists = git(['rev-parse', '--verify', '--quiet', branch], this.repoRoot, { allowFailure: true }).ok;
214
+ const branchExists = this.git(['rev-parse', '--verify', '--quiet', branch], this.repoRoot, { allowFailure: true }).ok;
131
215
  const args = branchExists
132
216
  ? ['worktree', 'add', wtPath, branch]
133
217
  : ['worktree', 'add', '-b', branch, wtPath, baseRef];
134
- git(args, this.repoRoot);
218
+ this.git(args, this.repoRoot);
135
219
 
136
220
  // Persist the base as a resolved SHA. A symbolic ref is useless later: inside the worktree
137
221
  // `HEAD` moves with every commit the task makes, so diffing against it reports nothing. The
138
222
  // file also survives a process boundary, since grading may run separately from execution.
139
- const baseSha = git(['rev-parse', baseRef], this.repoRoot).stdout.trim();
223
+ const baseSha = this.git(['rev-parse', baseRef], this.repoRoot).stdout.trim();
140
224
  fs.writeFileSync(path.join(wtPath, BASE_FILE), `${baseSha}\n`);
141
225
  return wtPath;
142
226
  }
@@ -250,7 +334,7 @@ class WorktreeManager {
250
334
  /** Repo HEAD, or null outside a repository. Recorded for readability; the per-skill hashes are
251
335
  * the actual provenance, and they stay correct on a dirty tree where a commit id does not. */
252
336
  headCommit() {
253
- const res = git(['rev-parse', 'HEAD'], this.repoRoot, { allowFailure: true });
337
+ const res = this.git(['rev-parse', 'HEAD'], this.repoRoot, { allowFailure: true });
254
338
  return res.ok ? res.stdout.trim() : null;
255
339
  }
256
340
 
@@ -269,11 +353,54 @@ class WorktreeManager {
269
353
  const wtPath = this.pathFor(taskId);
270
354
  const branch = this.branchFor(taskId);
271
355
 
272
- git(['worktree', 'remove', '--force', wtPath], this.repoRoot, { allowFailure: true });
356
+ this.git(['worktree', 'remove', '--force', wtPath], this.repoRoot, { allowFailure: true });
273
357
  if (fs.existsSync(wtPath)) fs.rmSync(wtPath, { recursive: true, force: true });
274
- git(['worktree', 'prune'], this.repoRoot, { allowFailure: true });
358
+ this.dropStaleRecord(wtPath);
275
359
  // Deleting the branch is what keeps repeated bench runs from accumulating one branch per case.
276
- git(['branch', '-D', branch], this.repoRoot, { allowFailure: true });
360
+ this.git(['branch', '-D', branch], this.repoRoot, { allowFailure: true });
361
+ }
362
+
363
+ /**
364
+ * `remove`'s steps, then a reading of what is actually gone (082 IC-021). Never throws for a
365
+ * failed step: the failure is an entry in `errors` and a false `verified`, so a caller can keep
366
+ * the worktree and list it by path instead of assuming it was removed.
367
+ */
368
+ removeVerified(taskId ) {
369
+ const wtPath = this.pathFor(taskId);
370
+ const branch = this.branchFor(taskId);
371
+ const errors = [];
372
+ let threw = false;
373
+ const step = (args ) => {
374
+ const res = this.git(args, this.repoRoot, { allowFailure: true });
375
+ if (!res.ok) errors.push(res.code );
376
+ return res;
377
+ };
378
+
379
+ step(['worktree', 'remove', '--force', wtPath]);
380
+ if (fs.existsSync(wtPath)) {
381
+ try {
382
+ fs.rmSync(wtPath, { recursive: true, force: true });
383
+ } catch (err ) {
384
+ threw = true;
385
+ errors.push(String(err?.code ?? 'unknown'));
386
+ }
387
+ }
388
+ this.dropStaleRecord(wtPath);
389
+ // `branch -D` fails harmlessly when the branch never existed; only the reading below decides.
390
+ step(['branch', '-D', branch]);
391
+
392
+ const pathGone = !fs.existsSync(wtPath);
393
+ const branchGone = !this.git(['rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], this.repoRoot, { allowFailure: true }).ok;
394
+ const unregistered = !this.isRegistered(wtPath);
395
+ return {
396
+ path: wtPath,
397
+ branch,
398
+ pathGone,
399
+ branchGone,
400
+ unregistered,
401
+ verified: !threw && pathGone && branchGone && unregistered,
402
+ errors,
403
+ };
277
404
  }
278
405
 
279
406
  /**
@@ -292,9 +419,9 @@ class WorktreeManager {
292
419
  if (!base) {
293
420
  return { diff: null, untracked: [], error: `no recorded base for ${taskId}; pass baseRef explicitly` };
294
421
  }
295
- const committed = git(['diff', `${base}...HEAD`], wtPath, { allowFailure: true });
296
- const working = git(['diff', 'HEAD'], wtPath, { allowFailure: true });
297
- const untracked = git(['ls-files', '--others', '--exclude-standard'], wtPath, { allowFailure: true });
422
+ const committed = this.git(['diff', `${base}...HEAD`], wtPath, { allowFailure: true });
423
+ const working = this.git(['diff', 'HEAD'], wtPath, { allowFailure: true });
424
+ const untracked = this.git(['ls-files', '--others', '--exclude-standard'], wtPath, { allowFailure: true });
298
425
 
299
426
  if (!committed.ok && !working.ok) {
300
427
  return { diff: null, untracked: [], error: committed.stderr || working.stderr };
@@ -174,7 +174,7 @@ function migrateLegacyManifest({ root, scope, scopeRoot, claudeDir, now = new Da
174
174
  for (const resource of resources) if (!existing.has(ownershipKey(resource))) current.resources.push(resource);
175
175
 
176
176
  // The legacy manifest's MCP selection was owned by Claude's config flow. Do not infer an
177
- // equivalent selection for Codex or Gemini merely because they happened to share the old file.
177
+ // equivalent selection for Codex merely because it happened to share the old file.
178
178
  if (Array.isArray(legacy.mcpServers) && !Object.hasOwn(current.mcpSelections, 'claude')) current.mcpSelections.claude = [...legacy.mcpServers];
179
179
  for (const [harness, status] of Object.entries(legacy.tools) ) {
180
180
  if (status?.installed && !current.targets[harness]) current.targets[harness] = { installed: true, lastUpdated: status.last_updated ?? null, imported: true };
@@ -11,8 +11,13 @@ const fs = require('node:fs');
11
11
  const os = require('node:os');
12
12
  const path = require('node:path');
13
13
  const { atomicJsonWrite } = require('./index.js');
14
+ const { isPackageId } = require('../helper/package-ids.js');
14
15
 
15
- const LOCK_VERSION = 1;
16
+ // Version plan (122 IC-007): a lock holding no third-party package stays version 1, byte for byte what
17
+ // every earlier DoFlow wrote and reads; one holding packages is version 2, which an older DoFlow
18
+ // refuses with its existing `Unsupported doflow.lock version` (the fail-closed direction).
19
+ const LOCK_VERSION = 2;
20
+ const LOCK_VERSION_BASE = 1;
16
21
  const LOCK_FILE = 'doflow.lock';
17
22
 
18
23
  function lockRoot({ scope, projectRoot = '.', homeDir = os.homedir() } ) {
@@ -31,7 +36,7 @@ function stable(value ) {
31
36
  function defaultLock({ scope, scopeRoot } ) {
32
37
  if (scope !== 'project' && scope !== 'global') throw new Error(`Invalid lock scope: '${scope}'`);
33
38
  return {
34
- version: LOCK_VERSION,
39
+ version: LOCK_VERSION_BASE,
35
40
  scope,
36
41
  scopeRoot: path.resolve(scopeRoot),
37
42
  generatedAt: null,
@@ -42,9 +47,46 @@ function defaultLock({ scope, scopeRoot } )
42
47
  };
43
48
  }
44
49
 
50
+ const DIGEST = /^[0-9a-f]{64}$/;
51
+ const ISO_UTC = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
52
+ const SOURCE_TYPES = ['local', 'git', 'npm', 'bundled'];
53
+ const PACKAGE_FIELDS = ['id', 'version', 'source', 'contentDigest', 'capabilityDigest', 'enabled', 'addedAt'];
54
+
55
+ /** The package rows of a lock: a v1 lock, an absent section and no lock all read as `[]`. */
56
+ function lockPackages(lock ) {
57
+ return Array.isArray(lock?.packages) ? lock.packages : [];
58
+ }
59
+
60
+ /** Refuse a malformed `packages` section, naming the field. The row is a closed shape (IC-007). */
61
+ function validatePackages(value ) {
62
+ if (value.scope === 'project') throw new Error('Invalid doflow.lock: packages is not allowed in a project-scope lock');
63
+ if (!Array.isArray(value.packages)) throw new Error('Invalid doflow.lock: packages must be an array');
64
+ const seen = new Set ();
65
+ value.packages.forEach((row , index ) => {
66
+ const at = `packages[${index}]`;
67
+ const bad = (field , why ) => new Error(`Invalid doflow.lock: ${at}.${field} ${why}`);
68
+ if (!row || typeof row !== 'object' || Array.isArray(row)) throw new Error(`Invalid doflow.lock: ${at} must be an object`);
69
+ for (const key of Object.keys(row)) if (!PACKAGE_FIELDS.includes(key)) throw bad(key, 'is not a package field');
70
+ if (!isPackageId(row.id)) throw bad('id', 'must be a package id');
71
+ if (seen.has(row.id)) throw bad('id', `'${row.id}' is a duplicate`);
72
+ seen.add(row.id);
73
+ if (typeof row.version !== 'string' || row.version === '') throw bad('version', 'must be a non-empty string');
74
+ if (!row.source || typeof row.source !== 'object' || Array.isArray(row.source) || !SOURCE_TYPES.includes(row.source.type)) {
75
+ throw bad('source', `must be an object whose type is one of ${SOURCE_TYPES.join(', ')}`);
76
+ }
77
+ for (const field of ['contentDigest', 'capabilityDigest']) {
78
+ if (typeof row[field] !== 'string' || !DIGEST.test(row[field])) throw bad(field, 'must be 64 lowercase hex characters');
79
+ }
80
+ if (typeof row.enabled !== 'boolean') throw bad('enabled', 'must be a boolean');
81
+ if (typeof row.addedAt !== 'string' || !ISO_UTC.test(row.addedAt) || Number.isNaN(Date.parse(row.addedAt))) {
82
+ throw bad('addedAt', 'must be an ISO 8601 UTC time');
83
+ }
84
+ });
85
+ }
86
+
45
87
  function validateLock(value ) {
46
88
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid doflow.lock: expected object');
47
- if (value.version !== LOCK_VERSION) throw new Error(`Unsupported doflow.lock version: '${value.version}'`);
89
+ if (value.version !== LOCK_VERSION_BASE && value.version !== LOCK_VERSION) throw new Error(`Unsupported doflow.lock version: '${value.version}'`);
48
90
  if (value.scope !== 'project' && value.scope !== 'global') throw new Error(`Invalid doflow.lock scope: '${value.scope}'`);
49
91
  if (typeof value.scopeRoot !== 'string' || !path.isAbsolute(value.scopeRoot)) throw new Error('Invalid doflow.lock: scopeRoot must be absolute');
50
92
  for (const field of ['targets', 'assets']) {
@@ -58,6 +100,7 @@ function validateLock(value ) {
58
100
  throw new Error(`Invalid doflow.lock: mcpSelections.${harness} must be an array of server ids`);
59
101
  }
60
102
  }
103
+ if ('packages' in value) validatePackages(value);
61
104
  return value;
62
105
  }
63
106
 
@@ -72,7 +115,14 @@ function writeLock(options , lock , { fsImpl = fs } =
72
115
  // Validate the caller's document first so a foreign version fails loudly here rather than
73
116
  // being silently normalized into something no future reader can distinguish.
74
117
  validateLock(lock);
75
- return atomicJsonWrite(lockPath(options), stable({ ...lock, version: LOCK_VERSION }), { fsImpl });
118
+ // The `packages` key is written only when a package is recorded, and then the file is version 2;
119
+ // otherwise it is the version 1 bytes every earlier DoFlow wrote. `stable` orders arrays by their
120
+ // JSON text, so the rows are put back in `id` order afterwards.
121
+ const packages = lockPackages(lock);
122
+ const { packages: _omit, ...rest } = lock;
123
+ const document = stable(packages.length ? { ...rest, version: LOCK_VERSION, packages } : { ...rest, version: LOCK_VERSION_BASE });
124
+ if (packages.length) document.packages = document.packages.sort((a , b ) => (a.id < b.id ? -1 : 1));
125
+ return atomicJsonWrite(lockPath(options), document, { fsImpl });
76
126
  }
77
127
 
78
128
  /** The lock's MCP rows for the harnesses it targets. A row naming a harness outside `targets` is
@@ -82,8 +132,11 @@ function pinnedSelections(lock ) {
82
132
  return Object.fromEntries(Object.entries(lock?.mcpSelections ?? {}).filter(([harness]) => targets.has(harness)));
83
133
  }
84
134
 
85
- /** Compare two lock documents. Arrays are keyed by identity (assets by id, targets by harness,
86
- * fallback to full JSON), mcpSelections by harness, where an absent row (`null`) and a recorded
135
+ /** The facts of a package row that make drift: its source and the time it was added are record keeping. */
136
+ const packageFacts = (row ) => ({ version: row.version, contentDigest: row.contentDigest, capabilityDigest: row.capabilityDigest, enabled: row.enabled });
137
+
138
+ /** Compare two lock documents. Arrays are keyed by identity (assets and packages by id, targets by
139
+ * harness, fallback to full JSON), packages on their `packageFacts` only, mcpSelections by harness, where an absent row (`null`) and a recorded
87
140
  * empty selection (`[]`) differ. Returns {added, removed, changed} per section plus a clean flag. */
88
141
  function diffLocks(previousLock , nextLock ) {
89
142
  // Compare in the order writeLock stores, so a document built in memory never differs from the
@@ -91,13 +144,13 @@ function diffLocks(previousLock , nextLock ) {
91
144
  const previous = previousLock && stable(previousLock);
92
145
  const next = nextLock && stable(nextLock);
93
146
  const key = (entry ) => entry?.id ?? entry?.harness ?? JSON.stringify(entry);
94
- const sectionDiff = (before , after ) => {
147
+ const sectionDiff = (before , after , compared = (entry) => entry) => {
95
148
  const beforeKeys = new Map ((before || []).map((entry ) => [key(entry), entry]));
96
149
  const afterKeys = new Map ((after || []).map((entry ) => [key(entry), entry]));
97
150
  const added = [...afterKeys].filter(([k]) => !beforeKeys.has(k)).map(([, v]) => v);
98
151
  const removed = [...beforeKeys].filter(([k]) => !afterKeys.has(k)).map(([, v]) => v);
99
152
  const changed = [...afterKeys]
100
- .filter(([k, v]) => beforeKeys.has(k) && JSON.stringify(beforeKeys.get(k)) !== JSON.stringify(v))
153
+ .filter(([k, v]) => beforeKeys.has(k) && JSON.stringify(compared(beforeKeys.get(k))) !== JSON.stringify(compared(v)))
101
154
  .map(([, v]) => v);
102
155
  return { added, removed, changed };
103
156
  };
@@ -110,13 +163,14 @@ function diffLocks(previousLock , nextLock ) {
110
163
  const result = {
111
164
  targets: sectionDiff(previous?.targets, next?.targets),
112
165
  assets: sectionDiff(previous?.assets, next?.assets),
166
+ packages: sectionDiff(previous?.packages, next?.packages, (row) => stable(packageFacts(row))),
113
167
  mcpSelections: { changed: mcpChanged },
114
168
  meta: {
115
169
  sourceVersion: (previous?.sourceVersion ?? null) !== (next?.sourceVersion ?? null)
116
170
  ? { before: previous?.sourceVersion ?? null, after: next?.sourceVersion ?? null } : null,
117
171
  },
118
172
  };
119
- result.clean = ['targets', 'assets'].every((name) => {
173
+ result.clean = ['targets', 'assets', 'packages'].every((name) => {
120
174
  const section = result[name];
121
175
  return section.added.length === 0 && section.removed.length === 0 && section.changed.length === 0;
122
176
  }) && result.mcpSelections.changed.length === 0 && result.meta.sourceVersion === null;
@@ -132,5 +186,5 @@ function removeLock(options , { fsImpl = fs } = {})
132
186
  }
133
187
 
134
188
  module.exports = {
135
- LOCK_VERSION, LOCK_FILE, lockRoot, lockPath, defaultLock, validateLock, readLock, writeLock, pinnedSelections, diffLocks, removeLock,
189
+ LOCK_VERSION, LOCK_VERSION_BASE, LOCK_FILE, lockRoot, lockPath, defaultLock, validateLock, lockPackages, readLock, writeLock, pinnedSelections, diffLocks, removeLock,
136
190
  };
@@ -404,5 +404,5 @@ function holdHandle({ fsImpl, root, stateDir, lockPath, token, hostname, firstCr
404
404
  }
405
405
 
406
406
  module.exports = {
407
- acquireRunLock, RunLockTimeoutError, RunLockError, RunLockLostError, runLockPath, formatRunLockMessage,
407
+ acquireRunLock, RunLockTimeoutError, RunLockError, RunLockLostError, runLockPath, formatRunLockMessage, defaultIsAlive,
408
408
  };
@@ -5,26 +5,80 @@
5
5
  // the surface the migrated modules actually touch. Extend it as more modules migrate, not ahead of
6
6
  // need. See agent-docs/BRIEF_GOAL_DOFLOW.md for the migration roadmap.
7
7
 
8
- declare var require: { (id: string): any; main: any };
8
+ type TestCallback = (t?: any, done?: any) => any;
9
+ interface TestFn {
10
+ (name: string, fn?: TestCallback): any;
11
+ (name: string, opts: any, fn?: TestCallback): any;
12
+ (fn: TestCallback): any;
13
+ skip: TestFn;
14
+ todo: TestFn;
15
+ only: TestFn;
16
+ }
17
+ interface NodeTestModule extends TestFn {
18
+ test: TestFn;
19
+ describe: TestFn;
20
+ it: TestFn;
21
+ suite: TestFn;
22
+ before(fn: TestCallback, opts?: any): void;
23
+ after(fn: TestCallback, opts?: any): void;
24
+ beforeEach(fn: TestCallback, opts?: any): void;
25
+ afterEach(fn: TestCallback, opts?: any): void;
26
+ mock: any;
27
+ run: any;
28
+ [key: string]: any;
29
+ }
30
+ interface NodeHttpModule {
31
+ createServer(listener?: (req?: any, res?: any) => any): any;
32
+ [key: string]: any;
33
+ }
34
+ interface NodeNetModule {
35
+ createServer(opts?: any, listener?: (socket?: any) => any): any;
36
+ [key: string]: any;
37
+ }
38
+
39
+ declare var require: {
40
+ (id: 'node:test'): NodeTestModule;
41
+ (id: 'node:http' | 'http'): NodeHttpModule;
42
+ (id: 'node:net' | 'net'): NodeNetModule;
43
+ (id: string): any;
44
+ main: any;
45
+ resolve(id: string, options?: any): string;
46
+ cache: Record<string, any>;
47
+ };
9
48
 
10
- declare var module: { exports: any };
49
+ declare var module: { exports: any; parent?: any; filename?: string; [key: string]: any };
11
50
 
12
51
  declare var process: {
13
52
  features?: { typescript?: false | 'strip' | 'transform' };
14
53
  env: Record<string, string | undefined>;
15
54
  platform: string;
16
55
  execPath: string;
56
+ execArgv: string[];
17
57
  version: string;
18
58
  pid: number;
59
+ ppid: number;
19
60
  argv: string[];
20
61
  cwd(): string;
21
62
  exit(code?: number): never;
22
63
  exitCode?: number;
23
64
  kill(pid: number, signal?: string | number): void;
24
65
  hrtime: { (time?: [number, number]): [number, number]; bigint(): bigint };
25
- stdin: { isTTY?: boolean; isRaw?: boolean; setRawMode(mode: boolean): void };
26
- stdout: { write(chunk: string): boolean; isTTY?: boolean };
27
- stderr: { write(chunk: string): boolean };
66
+ stdin: { isTTY?: boolean; isRaw?: boolean; setRawMode(mode: boolean): void; [key: string]: any };
67
+ stdout: { write(chunk: any, ...rest: any[]): boolean; isTTY?: boolean; columns?: number; [key: string]: any };
68
+ stderr: { write(chunk: any, ...rest: any[]): boolean; [key: string]: any };
69
+ arch: string;
70
+ versions: Record<string, string>;
71
+ memoryUsage(): { rss: number; heapTotal: number; heapUsed: number; external: number; [key: string]: number };
72
+ resourceUsage(): Record<string, number>;
73
+ uptime(): number;
74
+ umask(mask?: number): number;
75
+ getuid?(): number;
76
+ nextTick(callback: (...args: any[]) => void, ...args: any[]): void;
77
+ emitWarning(warning: any, ...rest: any[]): void;
78
+ once(event: string, listener: (...args: any[]) => void): any;
79
+ off(event: string, listener: (...args: any[]) => void): any;
80
+ emit(event: string, ...args: any[]): boolean;
81
+ chdir(directory: string): void;
28
82
  on(event: string, listener: (...args: any[]) => void): any;
29
83
  removeListener(event: string, listener: (...args: any[]) => void): any;
30
84
  };
@@ -38,25 +92,47 @@ declare var console: {
38
92
  log(...args: any[]): void;
39
93
  error(...args: any[]): void;
40
94
  warn(...args: any[]): void;
95
+ info(...args: any[]): void;
96
+ debug(...args: any[]): void;
97
+ table(...args: any[]): void;
98
+ [key: string]: any;
41
99
  };
42
100
 
43
101
  interface Buffer extends Uint8Array {
44
102
  toString(encoding?: string, start?: number, end?: number): string;
45
103
  equals(other: Uint8Array): boolean;
46
104
  subarray(begin?: number, end?: number): Buffer;
105
+ readUInt16LE(offset?: number): number;
106
+ readUInt32LE(offset?: number): number;
107
+ readInt32LE(offset?: number): number;
108
+ readBigInt64LE(offset?: number): bigint;
109
+ readUInt8(offset?: number): number;
110
+ readUInt16BE(offset?: number): number;
111
+ readUInt32BE(offset?: number): number;
112
+ writeUInt8(value: number, offset?: number): number;
113
+ writeUInt16LE(value: number, offset?: number): number;
114
+ writeUInt32LE(value: number, offset?: number): number;
115
+ writeUInt32BE(value: number, offset?: number): number;
116
+ write(string: string, offset?: number, length?: number, encoding?: string): number;
117
+ slice(begin?: number, end?: number): Buffer;
118
+ toJSON(): { type: 'Buffer'; data: number[] };
47
119
  }
48
120
  declare var Buffer: {
49
- alloc(size: number): Buffer;
50
- from(data: any, encoding?: string): Buffer;
121
+ alloc(size: number, fill?: any, encoding?: string): Buffer;
122
+ from(data: any, encodingOrOffset?: any, length?: number): Buffer;
51
123
  byteLength(string: string, encoding?: string): number;
52
124
  isBuffer(value: any): boolean;
53
125
  allocUnsafe(size: number): Buffer;
54
126
  concat(list: Uint8Array[], totalLength?: number): Buffer;
127
+ compare(a: Uint8Array, b: Uint8Array): number;
128
+ prototype: Buffer;
55
129
  };
56
130
 
57
131
  // Timers and WHATWG URL, as the migrated .ts modules use them (opaque handle; parsed URL fields only).
58
132
  declare function setTimeout(callback: (...args: any[]) => void, ms?: number, ...args: any[]): any;
59
133
  declare function clearTimeout(handle: any): void;
134
+ declare function setInterval(callback: (...args: any[]) => void, ms?: number, ...args: any[]): any;
135
+ declare function clearInterval(handle: any): void;
60
136
  declare var URL: {
61
137
  new (input: string, base?: string): { protocol: string; username: string; password: string; hostname: string; [key: string]: any };
62
138
  };
@@ -66,3 +142,26 @@ declare var performance: { now(): number };
66
142
 
67
143
  // structuredClone (workflow-orchestrator previewHandoff deep-copies a run program).
68
144
  declare function structuredClone<T>(value: T): T;
145
+
146
+ // Ambient globals used by test/ and bench/ (all present in Node >=24; declared loosely, no @types/node).
147
+ declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any;
148
+ declare function clearImmediate(handle: any): void;
149
+ declare function queueMicrotask(callback: () => void): void;
150
+ declare function fetch(input: any, init?: any): Promise<any>;
151
+ declare var AbortController: { new (): { signal: any; abort(reason?: any): void }; prototype: any };
152
+ declare var AbortSignal: { new (): any; abort(reason?: any): any; timeout(ms: number): any; any(signals: any[]): any; prototype: any };
153
+ declare var TextEncoder: { new (): { encode(input?: string): Uint8Array; encoding: string } };
154
+ declare var TextDecoder: { new (label?: string, options?: any): { decode(input?: any, options?: any): string; encoding: string } };
155
+ declare var URLSearchParams: { new (init?: any): any; prototype: any };
156
+ declare var EventTarget: { new (): any; prototype: any };
157
+ declare var Event: { new (type: string, init?: any): any; prototype: any };
158
+
159
+ interface ObjectConstructor {
160
+ entries(o: any): [string, any][];
161
+ values(o: any): any[];
162
+ fromEntries(entries: any): any;
163
+ }
164
+
165
+ interface ErrorConstructor {
166
+ stackTraceLimit?: number;
167
+ }
@@ -1,77 +0,0 @@
1
- {
2
- "description": "DoFlow lifecycle hooks for Gemini CLI. Gemini fingerprints hook name/command and warns before executing a changed one.",
3
- "hooks": {
4
- "SessionStart": [
5
- {
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "bash \"$GEMINI_PROJECT_DIR/.gemini/hooks/session-start.sh\""
10
- }
11
- ]
12
- }
13
- ],
14
- "SessionEnd": [
15
- {
16
- "hooks": [
17
- {
18
- "type": "command",
19
- "command": "bash \"$GEMINI_PROJECT_DIR/.gemini/hooks/session-end.sh\"",
20
- "timeout": 3000
21
- }
22
- ]
23
- }
24
- ],
25
- "BeforeTool": [
26
- {
27
- "matcher": "^run_shell_command$",
28
- "hooks": [
29
- {
30
- "type": "command",
31
- "command": "bash \"$GEMINI_PROJECT_DIR/.gemini/hooks/pre-bash-guard.sh\""
32
- }
33
- ]
34
- },
35
- {
36
- "matcher": "^mcp_.*",
37
- "hooks": [
38
- {
39
- "type": "command",
40
- "command": "bash \"$GEMINI_PROJECT_DIR/.gemini/hooks/mcp-tool-guard.sh\""
41
- }
42
- ]
43
- },
44
- {
45
- "matcher": "^(write_file|replace)$",
46
- "hooks": [
47
- {
48
- "type": "command",
49
- "command": "bash \"$GEMINI_PROJECT_DIR/.gemini/hooks/pre-implement-gate.sh\""
50
- }
51
- ]
52
- }
53
- ],
54
- "AfterTool": [
55
- {
56
- "matcher": "^(write_file|replace)$",
57
- "hooks": [
58
- {
59
- "type": "command",
60
- "command": "bash \"$GEMINI_PROJECT_DIR/.gemini/hooks/post-edit-lint.sh\""
61
- }
62
- ]
63
- }
64
- ],
65
- "PreCompress": [
66
- {
67
- "hooks": [
68
- {
69
- "type": "command",
70
- "command": "bash \"$GEMINI_PROJECT_DIR/.gemini/hooks/pre-compact.sh\"",
71
- "timeout": 3000
72
- }
73
- ]
74
- }
75
- ]
76
- }
77
- }