@khoavu882/doflow 2.0.0-beta.4 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  3. package/core/.antigravity-plugin/plugin.json +1 -3
  4. package/core/.claude-plugin/plugin.json +1 -1
  5. package/core/.codex-plugin/plugin.json +1 -1
  6. package/core/.plugin/plugin.json +1 -1
  7. package/core/harnesses/claude/hooks/lib.sh +43 -24
  8. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  9. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  10. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  11. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  12. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  13. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  14. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  15. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  16. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  17. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  18. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  19. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  20. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  21. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  22. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  23. package/core/harnesses/claude/settings/settings.json +18 -0
  24. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  25. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  26. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  27. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  28. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  29. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  30. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  31. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  32. package/core/harnesses/codex/hooks/lib.sh +44 -25
  33. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  34. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  35. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  36. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  37. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  38. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  39. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  40. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  41. package/core/harnesses/codex/hooks/subagent-audit.sh +18 -6
  42. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  43. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  44. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  45. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  46. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  47. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  48. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  49. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  50. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  51. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  52. package/core/harnesses/kiro/agents/doflow.json +1 -1
  53. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  54. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +21 -5
  55. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +21 -5
  56. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +21 -5
  57. package/core/harnesses/kiro/hooks/session-start.sh +12 -5
  58. package/core/harnesses/kiro/hooks/stop-check.sh +22 -30
  59. package/core/harnesses/pi/extensions/doflow-guard.js +167 -0
  60. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -12
  61. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  62. package/core/harnesses/shared/hooks/bridge/bridge.sh +277 -0
  63. package/core/harnesses/shared/hooks/bridge/envelope.jq +129 -0
  64. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  65. package/core/harnesses/shared/hooks/bridge/respond.sh +70 -0
  66. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  67. package/core/harnesses/shared/hooks/policies/capture-failure.sh +51 -16
  68. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  69. package/core/harnesses/shared/hooks/policies/contract.sh +78 -0
  70. package/core/harnesses/shared/hooks/policies/deny-json.sh +35 -9
  71. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  72. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  73. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  74. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  75. package/core/harnesses/shared/hooks/policies/lib.sh +65 -40
  76. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +48 -35
  77. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +284 -33
  78. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +180 -110
  79. package/core/harnesses/shared/hooks/policies/session-context.sh +66 -7
  80. package/core/harnesses/shared/hooks/policies/stop-check.sh +61 -23
  81. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +39 -6
  82. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  83. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +97 -159
  84. package/core/harnesses/shared/hooks/stream-hook-runner.js +114 -187
  85. package/core/harnesses/shared/locator/doflow-run +2 -3
  86. package/core/registry/advice.json +3 -3
  87. package/core/registry/assets.json +72 -80
  88. package/core/registry/capabilities.json +12 -0
  89. package/core/registry/contracts.json +60 -33
  90. package/core/registry/contribution-points.json +32 -0
  91. package/core/registry/gates.json +32 -0
  92. package/core/registry/guidance-caps.json +40 -0
  93. package/core/registry/harnesses.json +1542 -277
  94. package/core/registry/lifecycle.json +2837 -37
  95. package/core/registry/mcp.json +5 -1
  96. package/core/registry/models.json +7 -1
  97. package/core/registry/package-templates/README.md +19 -0
  98. package/core/registry/package-templates/agent/files/agent.md +9 -0
  99. package/core/registry/package-templates/agent/template.json +4 -0
  100. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  101. package/core/registry/package-templates/capability-provider/template.json +11 -0
  102. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  103. package/core/registry/package-templates/debate-preset/template.json +4 -0
  104. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  105. package/core/registry/package-templates/review-preset/template.json +4 -0
  106. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  107. package/core/registry/package-templates/skill/template.json +4 -0
  108. package/core/registry/package-templates/worker/files/worker.json +92 -0
  109. package/core/registry/package-templates/worker/template.json +11 -0
  110. package/core/registry/packages/doflow/doflow-package.json +15 -0
  111. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  112. package/core/registry/protocols.json +61 -0
  113. package/core/registry/routes.json +17 -0
  114. package/core/registry/rules.json +431 -0
  115. package/core/registry/settings.json +276 -0
  116. package/core/registry/storage.json +701 -30
  117. package/core/registry/telemetry.json +236 -0
  118. package/core/registry/transcripts.json +142 -0
  119. package/core/registry/verification.json +76 -1
  120. package/core/registry/workflows.json +55 -0
  121. package/core/shared/agent-specs/core-implementer.md +3 -0
  122. package/core/shared/agent-specs/delivery-lead.md +68 -0
  123. package/core/shared/agent-specs/quality-guardian.md +5 -1
  124. package/core/shared/agent-specs/research-writer.md +10 -3
  125. package/core/shared/agent-specs/reviewer.md +53 -0
  126. package/core/shared/agent-specs/spec-analyst.md +5 -6
  127. package/core/shared/agent-specs/system-architect.md +5 -1
  128. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  129. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  130. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  131. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  132. package/core/shared/guidance/pointers/codex.md +1 -4
  133. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  134. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  135. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  136. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  137. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  138. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  139. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  140. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  141. package/core/shared/guidance/rules/RULES.md +45 -0
  142. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  143. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  144. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  145. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  146. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  147. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  148. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  149. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  150. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  151. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  152. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  153. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  154. package/core/shared/scripts/doflow/bin/doflow-run +154 -35
  155. package/core/shared/skills/do/SKILL.md +12 -10
  156. package/core/shared/skills/do/references/advice_calls.md +26 -0
  157. package/core/shared/skills/do/references/pm_routing.md +1 -2
  158. package/core/shared/skills/do/references/program_mode.md +31 -0
  159. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  160. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  161. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  162. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  163. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  164. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  165. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  166. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  167. package/core/shared/skills/do-design/SKILL.md +24 -23
  168. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  169. package/core/shared/skills/do-document/SKILL.md +11 -1
  170. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  171. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  172. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  173. package/core/shared/skills/do-execute-plan/SKILL.md +17 -59
  174. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  175. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  176. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +28 -1
  177. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  178. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  179. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  180. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  181. package/core/shared/skills/do-flow/SKILL.md +8 -9
  182. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  183. package/core/shared/skills/do-git/SKILL.md +4 -0
  184. package/core/shared/skills/do-implement/SKILL.md +7 -9
  185. package/core/shared/skills/do-plan/SKILL.md +29 -19
  186. package/core/shared/skills/do-test/SKILL.md +7 -4
  187. package/core/shared/templates/doflow/constitution-template.md +9 -3
  188. package/core/shared/templates/doflow/data-model-template.md +1 -1
  189. package/core/shared/templates/doflow/design-template.md +113 -16
  190. package/core/shared/templates/doflow/plan-template.md +11 -10
  191. package/core/shared/templates/doflow/question-log-template.md +14 -2
  192. package/core/shared/templates/doflow/requirement-template.md +1 -1
  193. package/core/worker-evidence/antigravity/checks.json +14 -0
  194. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  195. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  196. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  197. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  198. package/core/worker-evidence/codex/checks.json +38 -0
  199. package/package.json +10 -6
  200. package/src/adapters/antigravity/index.js +226 -113
  201. package/src/adapters/claude/index.js +39 -5
  202. package/src/adapters/codex/agents.js +25 -15
  203. package/src/adapters/codex/hooks.js +40 -5
  204. package/src/adapters/codex/index.js +100 -22
  205. package/src/adapters/codex/native-projection.js +6 -2
  206. package/src/adapters/copilot/index.js +58 -23
  207. package/src/adapters/copy-tree.js +294 -48
  208. package/src/adapters/guidance-manifest.js +71 -0
  209. package/src/adapters/guidance-pointer.js +19 -0
  210. package/src/adapters/hook-commands.js +6 -10
  211. package/src/adapters/index.js +1 -0
  212. package/src/adapters/kiro/index.js +38 -13
  213. package/src/adapters/opencode/index.js +8 -8
  214. package/src/adapters/pi/hooks.js +120 -0
  215. package/src/adapters/pi/index.js +48 -16
  216. package/src/adapters/resolve-source.js +26 -2
  217. package/src/cli/commands/install.js +3 -2
  218. package/src/cli/commands/reconcile.js +53 -9
  219. package/src/cli/commands/remove.js +2 -2
  220. package/src/cli/commands/rollback.js +6 -3
  221. package/src/cli/commands/self-update.js +18 -9
  222. package/src/cli/commands/status.js +21 -2
  223. package/src/cli/commands/ui.js +99 -0
  224. package/src/cli/commands/update.js +3 -2
  225. package/src/cli/index.js +338 -33
  226. package/src/cli/runtime-commands.js +243 -52
  227. package/src/cli/shared.js +0 -2
  228. package/src/helper/git.js +1 -1
  229. package/src/helper/guardrails.js +1 -1
  230. package/src/helper/marker-merge.js +31 -13
  231. package/src/helper/package-ids.js +37 -0
  232. package/src/helper/semver.js +94 -0
  233. package/src/install/backup.js +105 -15
  234. package/src/install/context.js +1 -1
  235. package/src/install/targets.js +31 -16
  236. package/src/lifecycle/coverage.js +74 -0
  237. package/src/lifecycle/hook-step.js +61 -0
  238. package/src/lifecycle/hook-tier.js +160 -0
  239. package/src/lifecycle/index.js +434 -86
  240. package/src/lifecycle/retired-harnesses.js +318 -0
  241. package/src/lifecycle/view.js +35 -15
  242. package/src/registry/index.js +686 -32
  243. package/src/registry/worker-ids.js +45 -0
  244. package/src/release/build-package.js +0 -3
  245. package/src/release/docs-blocks.js +492 -0
  246. package/src/release/preflight.js +425 -0
  247. package/src/release/rehearse.js +401 -0
  248. package/src/release/release-workflow.js +733 -0
  249. package/src/release/sync-plugin-versions.js +43 -7
  250. package/src/runtime/advice/bench-gate.js +25 -0
  251. package/src/runtime/advice/cli.js +59 -16
  252. package/src/runtime/advice/client.js +32 -9
  253. package/src/runtime/advice/config.js +98 -133
  254. package/src/runtime/advice/doctor.js +1 -1
  255. package/src/runtime/advice/elevation.js +97 -0
  256. package/src/runtime/advice/endpoint-guard.js +9 -3
  257. package/src/runtime/advice/mode.js +37 -0
  258. package/src/runtime/advice/record.js +18 -5
  259. package/src/runtime/advice/registry.js +4 -1
  260. package/src/runtime/advice/shadow-config.js +229 -0
  261. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  262. package/src/runtime/brief-rules.js +81 -0
  263. package/src/runtime/brief-tools.js +111 -34
  264. package/src/runtime/candidate.js +514 -0
  265. package/src/runtime/capabilities/executor.js +450 -0
  266. package/src/runtime/capabilities/loopback.js +41 -0
  267. package/src/runtime/capabilities/sanitizer.js +88 -0
  268. package/src/runtime/capability-router.js +135 -3
  269. package/src/runtime/checkouts.js +73 -11
  270. package/src/runtime/claims.js +27 -12
  271. package/src/runtime/cli-result.js +11 -4
  272. package/src/runtime/cli.js +221 -277
  273. package/src/runtime/config-home.js +42 -0
  274. package/src/runtime/context/arbitrate.js +767 -0
  275. package/src/runtime/context/budget.js +203 -0
  276. package/src/runtime/context/compose.js +1367 -0
  277. package/src/runtime/context/delivery-store.js +100 -0
  278. package/src/runtime/context/emission-rules.js +118 -0
  279. package/src/runtime/context/identity.js +227 -0
  280. package/src/runtime/context/mandatory.js +594 -0
  281. package/src/runtime/context/pointer.js +142 -0
  282. package/src/runtime/context/primary-command.js +117 -0
  283. package/src/runtime/context/primary.js +344 -0
  284. package/src/runtime/context/summary-store.js +655 -0
  285. package/src/runtime/context-pack.js +1106 -245
  286. package/src/runtime/decision-register.js +13 -4
  287. package/src/runtime/dispatch/admission.js +392 -0
  288. package/src/runtime/dispatch/attempt-end.js +274 -0
  289. package/src/runtime/dispatch/command.js +628 -0
  290. package/src/runtime/dispatch/controls.js +904 -0
  291. package/src/runtime/dispatch/envelope.js +276 -0
  292. package/src/runtime/dispatch/grants.js +310 -0
  293. package/src/runtime/dispatch/integrate.js +617 -0
  294. package/src/runtime/dispatch/loop.js +1835 -0
  295. package/src/runtime/dispatch/store.js +352 -0
  296. package/src/runtime/dispatch/verify-run.js +201 -0
  297. package/src/runtime/evidence-fields.js +39 -0
  298. package/src/runtime/evidence-ledger.js +41 -7
  299. package/src/runtime/failure/capture.js +3 -1
  300. package/src/runtime/failure/cli.js +25 -9
  301. package/src/runtime/failure/home.js +57 -30
  302. package/src/runtime/failure/store.js +7 -2
  303. package/src/runtime/feature-pointer.js +174 -0
  304. package/src/runtime/feature-resolve.js +13 -2
  305. package/src/runtime/fleet/command.js +77 -0
  306. package/src/runtime/fleet/projects.js +178 -0
  307. package/src/runtime/fleet/tree.js +383 -0
  308. package/src/runtime/freshness.js +77 -10
  309. package/src/runtime/git-memo.js +46 -0
  310. package/src/runtime/guidance/ablation.js +427 -0
  311. package/src/runtime/health.js +565 -17
  312. package/src/runtime/history-compactor.js +13 -3
  313. package/src/runtime/implementation-gate.js +277 -22
  314. package/src/runtime/inventory/coverage.js +1 -1
  315. package/src/runtime/inventory/index.js +1 -1
  316. package/src/runtime/inventory/precedence.js +2 -2
  317. package/src/runtime/inventory/siblings.js +6 -4
  318. package/src/runtime/knowledge/code-corpus.js +277 -0
  319. package/src/runtime/knowledge/code-lexical.js +130 -0
  320. package/src/runtime/knowledge/code-retrieval.js +307 -0
  321. package/src/runtime/knowledge/code-validation.js +106 -0
  322. package/src/runtime/knowledge/gp3-gate.js +74 -0
  323. package/src/runtime/knowledge/graph-adapter.js +288 -0
  324. package/src/runtime/knowledge/graph-fallback.js +394 -0
  325. package/src/runtime/knowledge/hybrid.js +136 -8
  326. package/src/runtime/knowledge/retrieval.js +34 -1
  327. package/src/runtime/knowledge/rtk-compress.js +77 -0
  328. package/src/runtime/knowledge/semble-bridge.py +106 -0
  329. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  330. package/src/runtime/knowledge/semble-provider.js +365 -0
  331. package/src/runtime/lifecycle/cli.js +16 -6
  332. package/src/runtime/lifecycle/event-store.js +8 -6
  333. package/src/runtime/lifecycle/followup.js +6 -1
  334. package/src/runtime/lifecycle/overview.js +24 -1
  335. package/src/runtime/lifecycle/report-store.js +8 -10
  336. package/src/runtime/lifecycle/retention.js +95 -31
  337. package/src/runtime/lifecycle/root.js +9 -1
  338. package/src/runtime/lifecycle/status.js +34 -17
  339. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  340. package/src/runtime/mask.js +134 -7
  341. package/src/runtime/mcp-consultations.js +40 -6
  342. package/src/runtime/model-router.js +79 -7
  343. package/src/runtime/models/catalogue.js +191 -0
  344. package/src/runtime/models/command.js +153 -0
  345. package/src/runtime/off-words.js +12 -0
  346. package/src/runtime/outcome.js +3 -3
  347. package/src/runtime/packages/command.js +1009 -0
  348. package/src/runtime/packages/contributions.js +116 -0
  349. package/src/runtime/packages/digest.js +132 -0
  350. package/src/runtime/packages/fetch.js +182 -0
  351. package/src/runtime/packages/grants.js +192 -0
  352. package/src/runtime/packages/index-file.js +134 -0
  353. package/src/runtime/packages/instruction.js +90 -0
  354. package/src/runtime/packages/manifest.js +331 -0
  355. package/src/runtime/packages/points.js +477 -0
  356. package/src/runtime/packages/programs.js +216 -0
  357. package/src/runtime/packages/scaffold.js +277 -0
  358. package/src/runtime/packages/source.js +90 -0
  359. package/src/runtime/packages/state.js +164 -0
  360. package/src/runtime/packages/store.js +449 -0
  361. package/src/runtime/packages/tar.js +106 -0
  362. package/src/runtime/packages/view.js +406 -0
  363. package/src/runtime/plan-check.js +59 -0
  364. package/src/runtime/plan-content.js +324 -0
  365. package/src/runtime/policy-init.js +118 -0
  366. package/src/runtime/profiles/doctor.js +96 -0
  367. package/src/runtime/profiles/resolve.js +244 -0
  368. package/src/runtime/profiles/rollback.js +254 -0
  369. package/src/runtime/profiles/sampler.js +41 -0
  370. package/src/runtime/readiness-record.js +16 -11
  371. package/src/runtime/readiness.js +127 -16
  372. package/src/runtime/research-request.js +50 -25
  373. package/src/runtime/retrieval-plan.js +402 -73
  374. package/src/runtime/retrieval-record.js +293 -0
  375. package/src/runtime/review/command.js +246 -0
  376. package/src/runtime/review/dominance.js +68 -0
  377. package/src/runtime/review/findings.js +434 -0
  378. package/src/runtime/review/identity.js +255 -0
  379. package/src/runtime/review/protocol-command.js +109 -0
  380. package/src/runtime/review/protocols.js +405 -0
  381. package/src/runtime/review/resolve-protocol.js +302 -0
  382. package/src/runtime/review/start.js +530 -0
  383. package/src/runtime/roadmap/facts.js +479 -0
  384. package/src/runtime/roadmap/gates.js +270 -0
  385. package/src/runtime/roadmap/loop.js +1636 -0
  386. package/src/runtime/roadmap/merge.js +381 -0
  387. package/src/runtime/roadmap/rows.js +1113 -0
  388. package/src/runtime/roadmap/seed.js +951 -0
  389. package/src/runtime/rules-report.js +336 -0
  390. package/src/runtime/scaffold/artifacts.js +63 -9
  391. package/src/runtime/scaffold/generate.js +21 -9
  392. package/src/runtime/settings/autopilot.js +106 -0
  393. package/src/runtime/settings/cli.js +280 -0
  394. package/src/runtime/settings/config-write.js +314 -0
  395. package/src/runtime/settings/files.js +271 -0
  396. package/src/runtime/settings/legacy.js +154 -0
  397. package/src/runtime/settings/lexical.js +56 -0
  398. package/src/runtime/settings/prepared-run.js +269 -0
  399. package/src/runtime/settings/project-layers.js +118 -0
  400. package/src/runtime/settings/refs.js +288 -0
  401. package/src/runtime/settings/registry.js +553 -0
  402. package/src/runtime/settings/resolve.js +713 -0
  403. package/src/runtime/settings/shared.js +37 -0
  404. package/src/runtime/settings/worker-billing.js +107 -0
  405. package/src/runtime/settings/write.js +269 -0
  406. package/src/runtime/slot-command.js +78 -0
  407. package/src/runtime/slots.js +671 -0
  408. package/src/runtime/source-version.js +290 -0
  409. package/src/runtime/state-root.js +217 -0
  410. package/src/runtime/storage/cli.js +12 -5
  411. package/src/runtime/storage/engine.js +9 -2
  412. package/src/runtime/storage/registry.js +14 -1
  413. package/src/runtime/storage/roots.js +66 -16
  414. package/src/runtime/storage/rules.js +82 -10
  415. package/src/runtime/storage/upkeep.js +10 -2
  416. package/src/runtime/storage/work-status.js +245 -26
  417. package/src/runtime/task-attempts.js +589 -69
  418. package/src/runtime/task-classifier.js +47 -11
  419. package/src/runtime/task-graph.js +373 -0
  420. package/src/runtime/task-run.js +778 -0
  421. package/src/runtime/task-scope.js +202 -56
  422. package/src/runtime/task-sources.js +147 -0
  423. package/src/runtime/task-state.js +440 -65
  424. package/src/runtime/team/admission.js +301 -0
  425. package/src/runtime/team/envelope.js +324 -0
  426. package/src/runtime/team/review-guard.js +156 -0
  427. package/src/runtime/trace/analyze.js +427 -0
  428. package/src/runtime/trace/baseline.js +770 -0
  429. package/src/runtime/trace/ledger.js +63 -140
  430. package/src/runtime/trace/otlp-export.js +568 -0
  431. package/src/runtime/trace/otlp-map.js +481 -0
  432. package/src/runtime/trace/spans.js +745 -0
  433. package/src/runtime/trace/telemetry.js +225 -0
  434. package/src/runtime/trace/views.js +1 -0
  435. package/src/runtime/transcripts/command.js +98 -0
  436. package/src/runtime/transcripts/cursor.js +326 -0
  437. package/src/runtime/transcripts/read.js +126 -0
  438. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  439. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  440. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  441. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  442. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  443. package/src/runtime/transcripts/registry.js +161 -0
  444. package/src/runtime/transcripts/stats.js +161 -0
  445. package/src/runtime/verification/check-key.js +628 -0
  446. package/src/runtime/verification/contract-runner.js +546 -11
  447. package/src/runtime/verification/engine.js +499 -9
  448. package/src/runtime/verification/impact.js +617 -0
  449. package/src/runtime/verification/inflight.js +205 -0
  450. package/src/runtime/verification/registry.js +176 -0
  451. package/src/runtime/verification/reuse.js +184 -0
  452. package/src/runtime/verification/telemetry-emit.js +254 -0
  453. package/src/runtime/verification/test-selection.js +67 -7
  454. package/src/runtime/worker/active.js +90 -0
  455. package/src/runtime/worker/cancel.js +222 -0
  456. package/src/runtime/worker/catalogue.js +122 -0
  457. package/src/runtime/worker/command.js +677 -0
  458. package/src/runtime/worker/evidence.js +288 -0
  459. package/src/runtime/worker/launch.js +288 -0
  460. package/src/runtime/worker/models.js +137 -0
  461. package/src/runtime/worker/parsers.js +218 -0
  462. package/src/runtime/worker/probes/index.js +706 -0
  463. package/src/runtime/worker/process-tree.js +415 -0
  464. package/src/runtime/worker/quota.js +43 -0
  465. package/src/runtime/worker/readiness.js +713 -0
  466. package/src/runtime/worker/run-store.js +166 -0
  467. package/src/runtime/worker/supervisor.js +541 -0
  468. package/src/runtime/worker/worktree-check.js +252 -0
  469. package/src/runtime/workflow-engine.js +5 -1
  470. package/src/runtime/workflow-orchestrator.js +578 -63
  471. package/src/runtime/worktree.js +149 -22
  472. package/src/state/index.js +1 -1
  473. package/src/state/lockfile.js +64 -10
  474. package/src/state/run-lock.js +1 -1
  475. package/types/node-commonjs.d.ts +106 -7
  476. package/core/.antigravity-plugin/hooks.json +0 -39
  477. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  478. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  479. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -21
  480. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  481. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -21
  482. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  483. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -22
  484. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  485. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  486. package/core/harnesses/gemini/hooks/session-start.sh +0 -5
  487. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  488. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  489. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  490. package/core/harnesses/shared/hooks/hooks.json +0 -47
  491. package/core/shared/guidance/FLAGS.md +0 -25
  492. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  493. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  494. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  495. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  496. package/src/adapters/gemini/hooks.js +0 -210
  497. package/src/adapters/gemini/index.js +0 -390
@@ -0,0 +1,38 @@
1
+ {
2
+ "schema": "doflow-worker-checks-v1",
3
+ "harness": "codex",
4
+ "binary": "codex",
5
+ "version": { "argv": ["--version"], "expect": "^codex-cli \\d+\\.\\d+\\.\\d+" },
6
+ "checks": [
7
+ {
8
+ "id": "exec-one-shot",
9
+ "argv": ["exec", "--help"],
10
+ "expect": ["^Usage: codex exec \\[OPTIONS\\] \\[PROMPT\\]", "instructions are read from stdin"],
11
+ "establishes": ["runnableProbe", "oneShot"]
12
+ },
13
+ {
14
+ "id": "exec-json",
15
+ "argv": ["exec", "--help"],
16
+ "expect": ["^\\s+--json$", "Print events to stdout as JSONL", "-o, --output-last-message"],
17
+ "establishes": ["structuredOutput"]
18
+ },
19
+ {
20
+ "id": "exec-sandbox",
21
+ "argv": ["exec", "--help"],
22
+ "expect": ["-s, --sandbox <SANDBOX_MODE>", "read-only, workspace-write"],
23
+ "establishes": ["readOnly", "isolatedEdit"]
24
+ },
25
+ {
26
+ "id": "spawn-denial",
27
+ "argv": ["features", "list", "--disable", "multi_agent", "--disable", "multi_agent_v2"],
28
+ "expect": ["^multi_agent\\s+\\S+\\s+false$", "^multi_agent_v2\\s+\\S+\\s+false$"],
29
+ "establishes": ["spawnDenial"]
30
+ },
31
+ {
32
+ "id": "exec-model",
33
+ "argv": ["exec", "--help"],
34
+ "expect": ["^\\s+-m, --model <MODEL>$"],
35
+ "establishes": ["model"]
36
+ }
37
+ ]
38
+ }
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@khoavu882/doflow",
3
- "version": "2.0.0-beta.4",
3
+ "version": "2.0.0",
4
4
  "description": "DoFlow config installer — npx CLI for the registry-driven multi-harness install/update/remove lifecycle",
5
5
  "keywords": [
6
6
  "claude-code",
7
7
  "codex",
8
- "gemini",
9
8
  "opencode",
10
9
  "ai-coding-agent",
11
10
  "skills",
@@ -26,16 +25,21 @@
26
25
  "doflow": "bin/doflow.js"
27
26
  },
28
27
  "engines": {
29
- "node": "^22.18.0 || >=24.3.0"
28
+ "node": ">=24.22.1"
30
29
  },
31
30
  "scripts": {
32
- "test": "node --test \"test/**/*.test.js\"",
31
+ "test": "node --test \"test/**/*.test.js\" \"test/**/*.test.ts\"",
32
+ "test:fast": "node test/helper/fast-tier.ts",
33
33
  "typecheck": "npx -y -p typescript@5.7.2 tsc -p tsconfig.json",
34
34
  "build:package": "node src/release/build-package.js .pack",
35
- "bench": "node bench/runner.js",
35
+ "bench": "node bench/runner.ts",
36
36
  "drift": "node scripts/check-format-drift.js",
37
+ "emission-rules": "node src/runtime/context/emission-rules.js --check",
37
38
  "release-notes": "node src/release/changelog-section.js",
38
- "version": "node src/release/sync-plugin-versions.js && git add core/.codex-plugin/plugin.json core/.claude-plugin/plugin.json core/.plugin/plugin.json"
39
+ "docs:blocks": "node src/release/docs-blocks.js --check",
40
+ "release:preflight": "node src/release/preflight.js",
41
+ "release:rehearse": "node src/release/rehearse.js",
42
+ "version": "node src/release/sync-plugin-versions.js && git add core/.codex-plugin/plugin.json core/.claude-plugin/plugin.json core/.plugin/plugin.json core/registry/packages/doflow/doflow-package.json core/registry/contribution-points.json"
39
43
  },
40
44
  "files": [
41
45
  "bin/",
@@ -4,9 +4,11 @@
4
4
 
5
5
  // Antigravity CLI adapter (#8). Scope follows the surfaces verified against Antigravity's own
6
6
  // docs (antigravity.google/docs/cli/*):
7
- // instructions — marker-managed section in PROJECT AGENTS.md only. Global memory is
8
- // ~/.gemini/GEMINI.md, shared with the Gemini CLI harness; two adapters writing one file with
9
- // one marker pair would fight, so global instructions are reported, never written.
7
+ // instructions — marker-managed section in project AGENTS.md and global ~/.gemini/GEMINI.md (left
8
+ // alone while another live harness declares it); a retired harness's span there is taken over in
9
+ // one write (row 127 IC-007), a person-edited file kept until --force. See instructionTarget.
10
+ // rules — the core set (DOFLOW_CORE.md and the files it imports), flat, in project .agents/rules
11
+ // only; the full guidance tree stays in .doflow/guidance.
10
12
  // skills — folder-form copy-tree into project .agents/skills or user ~/.gemini/config/skills.
11
13
  // agents — copy-tree into project .agents/agents or user ~/.gemini/config/agents.
12
14
  // mcp — entries of the mcpServers object in .agents/mcp_config.json (workspace) or
@@ -20,9 +22,10 @@ const fs = require('node:fs');
20
22
  const os = require('node:os');
21
23
  const path = require('node:path');
22
24
 
23
- const { MARKER_START, MARKER_END } = require('../../helper/marker-merge.js');
24
- const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, readJson, sourceDirFor } = require('../copy-tree.js');
25
+ const { MARKER_START, MARKER_END, removeMarkedSection } = require('../../helper/marker-merge.js');
26
+ const { planTree, applyTree, removeTree, verifyTree, resolveTransform, copyTreeAssets, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, readJson, sourceDirFor } = require('../copy-tree.js');
25
27
  const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
28
+ const { renderGuidancePointer } = require('../guidance-pointer.js');
26
29
  const { readMcpFiles, planMcpEntries, verifyMcpEntries, ownedMcpIds, writeMcpEntries } = require('../mcp-entries.js');
27
30
 
28
31
  const HARNESS = 'antigravity';
@@ -136,8 +139,8 @@ function sha256File(fsImpl , file ) {
136
139
 
137
140
  /** Native paths per scope, resolved from the declaration in core/registry/harnesses.json
138
141
  * ("paths"): project customization lives at <root>/.agents, global config at ~/.gemini/config
139
- * (the shared-customization root); instructions and skills are project-only surfaces, so no user
140
- * rule is declared for them and they resolve to null there. */
142
+ * (the shared-customization root); instructions are AGENTS.md in the project and ~/.gemini/GEMINI.md
143
+ * globally; the hook surfaces are project-only, so no user rule is declared and they resolve to null. */
141
144
  function resolveNativePaths(declaredPaths , { scope, scopeRoot, homeDir } = {}) {
142
145
  if (scope !== 'project' && scope !== 'global') throw new Error(`Unsupported Antigravity scope '${scope}'`);
143
146
  const resolved = resolveHarnessPaths(declaredPaths, { scope, scopeRoot, homeDir });
@@ -178,6 +181,8 @@ function createAntigravityAdapter({ declaredPaths = DEFAULT_DECLARED_PATHS }
178
181
  apply,
179
182
  remove,
180
183
  verify: (options , impl = {}) => verify(options, { ...impl, nativePaths: nativePathsFor }),
184
+ planHookDoors: (options , impl = {}) => planHookDoors(options, { ...impl, nativePaths: nativePathsFor }),
185
+ verifyHookDoors: (options , impl = {}) => verifyHookDoors(options, { ...impl, nativePaths: nativePathsFor }),
181
186
  };
182
187
  }
183
188
 
@@ -210,16 +215,25 @@ function render({ content = '' } = {}) {
210
215
  return `${MARKER_START}\n${String(content).trimEnd()}\n${MARKER_END}\n`;
211
216
  }
212
217
 
213
- function managedInstruction(existing , rendered ) {
218
+ const INSTRUCTIONS_IDENTITY = `doflow:${HARNESS}:instructions:managed-section`;
219
+ /** The short start marker the retired harness wrote into ~/.gemini/GEMINI.md (row 127). It is read
220
+ * only in a file this adapter is taking over, never written: the span it opens is replaced with ours. */
221
+ const PREDECESSOR_MARKER_START = '<!-- doflow:start -->';
222
+
223
+ /** The file's content with `rendered` as its DoFlow span: created, appended to a file with none, or
224
+ * replacing the span in place. `predecessor` also accepts the retired harness's start marker, for a
225
+ * file being taken over. `name` is the file's name in the conflict. */
226
+ function managedInstruction(existing , rendered , { name = 'AGENTS.md', predecessor = false } = {}) {
214
227
  if (existing === null) return { ok: true, operation: 'create', content: rendered };
215
- const start = existing.indexOf(MARKER_START);
228
+ let start = existing.indexOf(MARKER_START);
229
+ if (start === -1 && predecessor) start = existing.indexOf(PREDECESSOR_MARKER_START);
216
230
  const end = existing.indexOf(MARKER_END);
217
231
  if (start === -1 && end === -1) {
218
232
  const separator = existing === '' || existing.endsWith('\n') ? '\n' : '\n\n';
219
233
  const content = `${existing}${separator}${rendered}`;
220
234
  return { ok: true, operation: 'merge', content };
221
235
  }
222
- if (start === -1 || end === -1 || end < start) return { ok: false, conflict: 'AGENTS.md has malformed DoFlow managed-section markers' };
236
+ if (start === -1 || end === -1 || end < start) return { ok: false, conflict: `${name} has malformed DoFlow managed-section markers` };
223
237
  const content = `${existing.slice(0, start)}${rendered}${existing.slice(end + MARKER_END.length).replace(/^\n?/, '')}`;
224
238
  return { ok: true, operation: content === existing ? 'none' : 'merge', content };
225
239
  }
@@ -277,7 +291,7 @@ function planTrees({ assets, paths, scope, neutralResources, removing, repoRoot,
277
291
  for (const { asset, destDir } of targets) {
278
292
  const sourceDir = sourceDirFor(asset, { repoRoot }, fsImpl, HARNESS);
279
293
  const previousResources = ledgerFileResources(neutralResources, HARNESS, asset.id);
280
- const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(neutralResources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, keepModified: !removing,
294
+ const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(neutralResources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, transform: asset.transform, select: asset.select, keepModified: !removing,
281
295
  // Forwarded so the CLI's --force reaches planTree's conflict check; omitting it let
282
296
  // planTree's own `force = false` default stand in silently. Gated on `!removing` for the
283
297
  // reason codex/index.ts states in full: force heals drift on apply, but a hand-edited file
@@ -295,6 +309,7 @@ function planTrees({ assets, paths, scope, neutralResources, removing, repoRoot,
295
309
  ownershipIdentity: `doflow:${HARNESS}:copy-tree:${asset.id}:${change.relPath}`,
296
310
  kind: 'copy-tree-file', identity: change.relPath,
297
311
  afterFingerprint: change.fingerprint, fingerprint: change.fingerprint, sourceVersion: 'registry-v1',
312
+ transformName: asset.transform || null,
298
313
  ...(change.kept ? { retained: true, retainedFor: [] } : {}),
299
314
  projection: { renderer: 'copy-tree' },
300
315
  });
@@ -313,7 +328,8 @@ function runTreeChanges(changes , mode ) {
313
328
  .filter((c ) => c.projection?.renderer === 'copy-tree' || c.kind === 'copy-tree-file')
314
329
  .map((c ) => ({ relPath: c.identity ?? c.relPath, target: c.target,
315
330
  source: c._text ? undefined : c.source, operation: c.operation,
316
- fingerprint: c.fingerprint, pruneGuard: c.pruneGuard, ...(c._text ? { _text: c._text } : {}) }));
331
+ fingerprint: c.fingerprint, pruneGuard: c.pruneGuard, transformName: c.transformName || null,
332
+ ...(c._text ? { _text: c._text } : {}) }));
317
333
  // Content-managed tree files (hook scripts): write rendered text with +x, not a byte copy.
318
334
  for (const t of treeChanges) {
319
335
  if (t._text === undefined) continue;
@@ -328,58 +344,100 @@ function runTreeChanges(changes , mode ) {
328
344
  const plain = treeChanges.filter((t ) => t._text === undefined);
329
345
  const writes = plain.filter((c ) => c.operation !== 'remove');
330
346
  const removals = plain.filter((c ) => c.operation === 'remove');
331
- const applied = writes.length ? applyTree({ changes: writes }).applied : 0;
347
+ // A projection that declares a transform (the rules' always_on header) is re-derived from the
348
+ // source at write time, so each change is applied under its own transform.
349
+ const byTransform = new Map ();
350
+ for (const change of writes) byTransform.set(change.transformName, [...(byTransform.get(change.transformName) || []), change]);
351
+ let applied = 0;
352
+ for (const [transformName, group] of byTransform) {
353
+ applied += applyTree({ changes: group, transform: transformName ? resolveTransform(transformName) : undefined }).applied;
354
+ }
332
355
  const removed = removals.length ? removeTree({ changes: removals }).removed : 0;
333
356
  return { applied, removed };
334
357
  })(),
335
358
  };
336
- const writes = treeChanges.filter((c ) => c.operation !== 'remove');
337
- const removals = treeChanges.filter((c ) => c.operation === 'remove');
338
- const applied = writes.length ? applyTree({ changes: writes }).applied : 0;
339
- const removed = removals.length ? removeTree({ changes: removals }).removed : 0;
340
- return { applied, removed };
341
359
  }
342
360
 
343
361
  // ---- instructions component ----
344
362
 
345
- function planInstructions({ paths, assets, removing, repoRoot, fsImpl = fs } ) {
346
- if (scopeOf(paths) !== 'project') return { changes: [], conflicts: [] };
347
- void repoRoot;
363
+ /** The instruction file this run manages, or null: project AGENTS.md as ever; the global GEMINI.md
364
+ * only when the pointer asset is selected and no other live harness in `registry` declares that same
365
+ * file (until the harness that owned it retires, its adapter keeps it and this one stays out). */
366
+ function instructionTarget({ paths, assets, registry } ) {
367
+ if (!paths.instruction) return null;
368
+ if (scopeOf(paths) === 'project') return paths.instruction;
369
+ if (!(assets ?? []).some((asset ) => asset.id === POINTER_ASSET_ID)) return null;
370
+ const declaredBy = (harness ) => {
371
+ if (harness?.id === HARNESS || !harness?.paths?.instruction) return false;
372
+ try { return resolveHarnessPaths({ instruction: harness.paths.instruction }, { scope: 'global', scopeRoot: paths.root, homeDir: paths.root }).instruction === paths.instruction; } catch { return false; }
373
+ };
374
+ return (registry?.harnesses ?? []).some(declaredBy) ? null : paths.instruction;
375
+ }
376
+
377
+ /** How this run stands to a section it took over (IC-007): the lifecycle's handover verdict for the
378
+ * file, this harness's own row on it, and whether a person-edited file stays as it is (`retained`:
379
+ * edited before the handover, or kept since, and no --force). `predecessor` lets the retired
380
+ * harness's span count as the section to replace; `keptRow` is a row recorded as kept. A handover
381
+ * file whose DoFlow span already is this harness's render was written by DoFlow itself (a run that
382
+ * threw after that write and before its ledger write), so it does not count as edited. */
383
+ function instructionState({ target, context, resources, paths, fsImpl = fs } ) {
384
+ const handover = (context.handovers ?? []).find((item ) => item.target === target) ?? null;
385
+ const ownRow = (resources ?? []).find((row ) => row.harness === HARNESS && row.target === target && row.ownershipIdentity === INSTRUCTIONS_IDENTITY) ?? null;
386
+ const ownWrite = () => {
387
+ const rendered = renderedInstruction(paths, fsImpl);
388
+ if (rendered === null || !fsImpl.existsSync(target)) return false;
389
+ const outcome = managedInstruction(fsImpl.readFileSync(target, 'utf8'), rendered);
390
+ return outcome.ok && outcome.operation === 'none';
391
+ };
392
+ const edited = (handover?.edited === true && !ownWrite()) || ownRow?.retainedEdit === true;
393
+ return { predecessor: Boolean(handover) || ownRow?.retainedEdit === true, retained: edited && context.force !== true,
394
+ keptRow: ownRow?.retainedEdit === true };
395
+ }
396
+
397
+ function instructionsChange(target , fields ) {
398
+ return { assetId: POINTER_ASSET_ID, target, ownershipIdentity: INSTRUCTIONS_IDENTITY,
399
+ kind: 'instructions-section', identity: path.basename(target), projection: { renderer: 'antigravity-instructions' }, ...fields };
400
+ }
401
+
402
+ function planInstructions({ paths, target, removing, state, fsImpl = fs } ) {
403
+ const none = { changes: [], conflicts: [], notices: [] };
404
+ if (!target) return none;
348
405
  if (removing) {
349
406
  // Removal must strip the section DoFlow owns while leaving foreign bytes untouched — same
350
407
  // contract as the other AGENTS-style adapters.
351
- if (!fsImpl.existsSync(paths.instruction)) return { changes: [], conflicts: [] };
352
- const text = fsImpl.readFileSync(paths.instruction, 'utf8');
353
- if (!text.includes(MARKER_START)) return { changes: [], conflicts: [] };
354
- return {
355
- changes: [{
356
- assetId: POINTER_ASSET_ID, target: paths.instruction, operation: 'remove',
357
- ownershipIdentity: `doflow:${HARNESS}:instructions:managed-section`,
358
- kind: 'instructions-section', identity: 'AGENTS.md',
359
- projection: { renderer: 'antigravity-instructions' },
360
- }],
361
- conflicts: [],
362
- };
408
+ if (!fsImpl.existsSync(target)) return none;
409
+ const text = fsImpl.readFileSync(target, 'utf8');
410
+ // A kept edited file with no span left still carries this harness's row: the remove change
411
+ // releases the row, and apply finds no span to strip, so the person's file is not touched.
412
+ if (!text.includes(MARKER_START) && !state.keptRow) return none;
413
+ return { ...none, changes: [instructionsChange(target, { operation: 'remove' })] };
363
414
  }
364
- const sourceText = pointerBody(fsImpl);
365
- if (!sourceText) return { changes: [], conflicts: [] };
366
- const existing = fsImpl.existsSync(paths.instruction) ? fsImpl.readFileSync(paths.instruction, 'utf8') : null;
367
- const outcome = managedInstruction(existing, render({ content: sourceText }));
368
- if (!outcome.ok) return { changes: [], conflicts: [outcome.conflict] };
369
- if (outcome.operation === 'none') return { changes: [], conflicts: [] };
415
+ if (state.retained) {
416
+ const shown = scopeOf(paths) === 'global' ? `~/${path.relative(paths.root, target)}` : path.relative(paths.root, target);
417
+ return { ...none, notices: [`kept ${shown}: changed before handover; --force replaces the DoFlow section`] };
418
+ }
419
+ const rendered = renderedInstruction(paths, fsImpl);
420
+ if (rendered === null) return none;
421
+ const existing = fsImpl.existsSync(target) ? fsImpl.readFileSync(target, 'utf8') : null;
422
+ const outcome = managedInstruction(existing, rendered, { name: path.basename(target), predecessor: state.predecessor });
423
+ if (!outcome.ok) return { ...none, conflicts: [outcome.conflict] };
424
+ if (outcome.operation === 'none') return none;
370
425
  return {
371
- changes: [{
372
- assetId: POINTER_ASSET_ID, target: paths.instruction, operation: existing === null ? 'create' : 'update',
373
- ownershipIdentity: `doflow:${HARNESS}:instructions:managed-section`,
374
- kind: 'instructions-section', identity: 'AGENTS.md',
426
+ ...none,
427
+ changes: [instructionsChange(target, {
428
+ operation: existing === null ? 'create' : 'update',
375
429
  afterFingerprint: fingerprint(outcome.content), sourceVersion: 'registry-v1',
376
- projection: { renderer: 'antigravity-instructions' },
377
430
  _content: outcome.content,
378
- }],
379
- conflicts: [],
431
+ })],
380
432
  };
381
433
  }
382
434
 
435
+ /** This scope's DoFlow section: the shared pointer rendered for the scope, inside the markers. */
436
+ function renderedInstruction(paths , fsImpl ) {
437
+ const sourceText = pointerBody(fsImpl);
438
+ return sourceText ? render({ content: renderGuidancePointer(sourceText, { scope: scopeOf(paths), scopeRoot: paths.root }) }) : null;
439
+ }
440
+
383
441
  function pointerBody(fsImpl ) {
384
442
  // The shared pointer prose ships once; read it through the same repo-relative anchor the other
385
443
  // AGENTS-style adapters use.
@@ -430,13 +488,18 @@ function plan(options = {}, impl = {}) {
430
488
  const selectedServers = Array.isArray(options.mcp) ? options.mcp : [];
431
489
  const discovery = options.discovery ?? discover(options, impl);
432
490
 
433
- const instructions = planInstructions({ paths, assets: options.assets, removing, repoRoot: context.repoRoot, fsImpl });
491
+ const instructionFile = instructionTarget({ paths, assets: options.assets, registry: options.registry });
492
+ const instructions = planInstructions({ paths, target: instructionFile, removing, fsImpl,
493
+ state: instructionState({ target: instructionFile, context, resources: neutralResources, paths, fsImpl }) });
434
494
  const trees = planTrees({ assets: options.assets, paths, scope, neutralResources, removing, repoRoot: context.repoRoot, force: context.force === true, fsImpl });
435
495
  const mcp = planMcpEntries({
436
496
  ...mcpEntriesInput({ paths, mcp: selectedServers, mcpAdoptable: options.mcpAdoptable, resources: neutralResources }),
437
497
  files: discovery.mcpSnapshot, removing,
438
498
  });
439
- const hooksPlan = planHooks({ paths, scope, neutralResources, removing, repoRoot: context.repoRoot, fsImpl });
499
+ // Hook tiers (maint-hooks): outside `hooks.harnesses` this plan carries no hook change; the lifecycle
500
+ // plans the removal of what an earlier run wrote, from the ledger, against the full registry.
501
+ const hooksPlan = context.hooksOff === true && !removing ? { changes: [], conflicts: [] }
502
+ : planHooks({ paths, scope, neutralResources, removing, repoRoot: context.repoRoot, fsImpl });
440
503
 
441
504
  const changes = [...instructions.changes, ...trees.changes, ...mcp.changes, ...hooksPlan.changes];
442
505
  const conflicts = [...instructions.conflicts, ...trees.conflicts, ...mcp.conflicts, ...hooksPlan.conflicts];
@@ -445,10 +508,33 @@ function plan(options = {}, impl = {}) {
445
508
  conflicts,
446
509
  prerequisites: [],
447
510
  requiredNativeResources: changes,
448
- notices: [...trees.notices, ...mcp.notices],
511
+ notices: [...instructions.notices, ...trees.notices, ...mcp.notices],
449
512
  };
450
513
  }
451
514
 
515
+ /** The hook front doors (IC-037): the shims and the hooks.json groups that run them, planned and
516
+ * verified alone. Project scope only, and only when the harness selected the hooks asset. */
517
+ function planHookDoors(options = {}, impl = {}) {
518
+ const fsImpl = impl.fsImpl || fs;
519
+ const context = options.context ?? {};
520
+ const scope = options.scope ?? 'project';
521
+ if (!(options.assets ?? []).some((asset ) => asset.id === HOOKS_ASSET_ID)) return { changes: [], conflicts: [], prerequisites: [], notices: [] };
522
+ const paths = (impl.nativePaths ?? singleton.nativePaths)({ ...options, scope, homeDir: context.homeDir });
523
+ const doors = planHooks({ paths, scope, neutralResources: ledgerResources(options), removing: false, repoRoot: context.repoRoot, fsImpl });
524
+ return { changes: doors.changes, conflicts: doors.conflicts, prerequisites: [], notices: [] };
525
+ }
526
+
527
+ function verifyHookDoors(options = {}, impl = {}) {
528
+ const fsImpl = impl.fsImpl || fs;
529
+ const context = options.context ?? {};
530
+ const scope = options.scope ?? 'project';
531
+ if (scope !== 'project' || !(options.assets ?? []).some((asset ) => asset.id === HOOKS_ASSET_ID)) return { ok: true, statuses: [], resources: [], conflicts: [] };
532
+ const paths = (impl.nativePaths ?? singleton.nativePaths)({ ...options, scope, homeDir: context.homeDir });
533
+ const doors = verifyHooks({ options, paths, context, fsImpl });
534
+ const conflicts = doors.statuses.filter((status ) => status.status === 'conflict').map((status ) => status.reason ?? status.identity);
535
+ return { ok: conflicts.length === 0, ...doors, conflicts };
536
+ }
537
+
452
538
  function apply(options = {}, impl = {}) {
453
539
  const fsImpl = impl.fsImpl || fs;
454
540
  // apply()/verify() are reached through the lifecycle with the same scope inputs plan() saw, so
@@ -460,14 +546,26 @@ function apply(options = {}, impl = {}) {
460
546
 
461
547
  for (const change of changes.filter((c ) => c.projection?.renderer === 'antigravity-instructions')) {
462
548
  if (change.operation === 'remove') {
463
- // pi precedent: strip only the managed span; the file survives even when nothing remains.
464
549
  if (!fsImpl.existsSync(change.target)) continue;
550
+ // The global file is the person's: the span goes with the one newline put before it, so their
551
+ // bytes come back exactly (and a file that held only the span goes with it). A kept file taken
552
+ // over from the retired harness may still hold that harness's span, under its short marker.
553
+ if ((options.scope ?? 'project') === 'global') { removeMarkedSection(change.target, { startMarkers: [MARKER_START, PREDECESSOR_MARKER_START] }); continue; }
554
+ // pi precedent: strip only the managed span; the file survives even when nothing remains.
465
555
  const next = strippedInstruction(fsImpl.readFileSync(change.target, 'utf8'));
466
556
  if (next !== null) fsImpl.writeFileSync(change.target, next, 'utf8');
467
557
  continue;
468
558
  }
559
+ // One rename onto the file: no reader, and no failure, ever sees it without its DoFlow span.
469
560
  fsImpl.mkdirSync(path.dirname(change.target), { recursive: true });
470
- fsImpl.writeFileSync(change.target, change._content ?? render({ content: '' }), 'utf8');
561
+ const tmp = `${change.target}.${process.pid}.tmp`;
562
+ try {
563
+ fsImpl.writeFileSync(tmp, change._content ?? render({ content: '' }), 'utf8');
564
+ fsImpl.renameSync(tmp, change.target);
565
+ } catch (error) {
566
+ fsImpl.rmSync(tmp, { force: true });
567
+ throw error;
568
+ }
471
569
  }
472
570
  for (const change of changes.filter((c ) => c.projection?.renderer === 'antigravity-hooks')) {
473
571
  if (change.kind === 'copy-tree-file') continue; // handled by runTreeChanges below
@@ -506,6 +604,60 @@ function remove(options = {}, impl = {}) {
506
604
  return apply(options, impl);
507
605
  }
508
606
 
607
+ /** The hook shims' bytes and the registered groups (project scope), as statuses and resources. */
608
+ function verifyHooks({ options, paths, context, fsImpl } ) {
609
+ const statuses = [];
610
+ const resources = [];
611
+ const hooksTarget = paths.hooksJson;
612
+ const scriptTargets = {
613
+ 'pre-implementation-gate.sh': paths.hookScript,
614
+ 'stop-check.sh': paths.stopHookScript,
615
+ };
616
+ // Source-side residue: reads the shims from THIS checkout (the authored source of the
617
+ // projection, not an install destination — the destination is declared as the paths above).
618
+ const scriptSourceDir = path.join(path.resolve(registryRepoRoot(options)), 'core', 'harnesses', 'antigravity', 'hooks');
619
+ const removingOp = (context.operation ?? options.operation ?? '') === 'remove';
620
+ const fingerprints = {};
621
+ let scriptsOk = true;
622
+ let anyPresent = false;
623
+ for (const identity of HOOK_SCRIPTS) {
624
+ let sourceFp = null;
625
+ try { sourceFp = sha256File(fsImpl, path.join(scriptSourceDir, identity)); } catch { /* checkout without that shim */ }
626
+ fingerprints[identity] = sourceFp;
627
+ const target = scriptTargets[identity];
628
+ const current = fsImpl.existsSync(target) ? sha256File(fsImpl, target) : null;
629
+ if (current !== null) anyPresent = true;
630
+ if (current === null || current !== sourceFp) scriptsOk = false;
631
+ }
632
+ const doc = readJsonObject(hooksTarget, { fsImpl });
633
+ const gate = doc?.[HOOKS_GROUP];
634
+ const stop = doc?.[STOP_HOOKS_GROUP];
635
+ const gateOk = Boolean(gate?.PreToolUse?.[0]?.hooks?.[0]?.command)
636
+ && gate.PreToolUse[0].hooks[0].command === scriptTargets['pre-implementation-gate.sh']
637
+ && gate.PreToolUse[0].matcher === GATE_MATCHER;
638
+ // Stop registration is matcher-free: handlers sit directly under the event key.
639
+ const stopOk = Array.isArray(stop?.Stop) && stop.Stop.length > 0
640
+ && stop.Stop[0]?.command === scriptTargets['stop-check.sh'];
641
+ const groupsOk = gateOk && stopOk;
642
+ statuses.push({ assetId: HOOKS_ASSET_ID, capability: 'hooks', status:
643
+ removingOp ? (!anyPresent && !groupsOk ? 'absent' : 'retained')
644
+ : (scriptsOk && groupsOk ? 'managed' : (!anyPresent && !groupsOk ? 'absent' : 'missing')),
645
+ ownershipIdentity: `${HARNESS}:hooks:registration`, target: hooksTarget });
646
+ if (!removingOp && scriptsOk && groupsOk) {
647
+ resources.push({ assetId: HOOKS_ASSET_ID, target: hooksTarget,
648
+ ownershipIdentity: `${HARNESS}:hooks:registration`, kind: 'hooks-json',
649
+ fingerprint: fingerprint(JSON.stringify(doc)), sourceVersion: 'registry-v1',
650
+ projection: { renderer: 'antigravity-hooks' } });
651
+ for (const identity of HOOK_SCRIPTS) {
652
+ resources.push({ assetId: HOOKS_ASSET_ID, target: scriptTargets[identity],
653
+ ownershipIdentity: `${HARNESS}:copy-tree:${HOOKS_ASSET_ID}:${identity}`,
654
+ kind: 'copy-tree-file', identity, fingerprint: fingerprints[identity],
655
+ sourceVersion: 'registry-v1', projection: { renderer: 'antigravity-hooks' } });
656
+ }
657
+ }
658
+ return { statuses, resources };
659
+ }
660
+
509
661
  function verify(options = {}, impl = {}) {
510
662
  const fsImpl = impl.fsImpl || fs;
511
663
  const context = options.context ?? {};
@@ -515,24 +667,28 @@ function verify(options = {}, impl = {}) {
515
667
  const statuses = [];
516
668
  const resources = [];
517
669
 
518
- const instruction = paths.instruction && fsImpl.existsSync(paths.instruction)
519
- ? fsImpl.readFileSync(paths.instruction, 'utf8') : null;
520
- const managed = instruction !== null && instruction.includes(MARKER_START) && instruction.includes(MARKER_END);
521
- if (paths.instruction) {
670
+ const instructionFile = instructionTarget({ paths, assets: options.assets, registry: options.registry });
671
+ const state = instructionState({ target: instructionFile, context, resources: ledgerResources(options), paths, fsImpl });
672
+ const instruction = instructionFile && fsImpl.existsSync(instructionFile)
673
+ ? fsImpl.readFileSync(instructionFile, 'utf8') : null;
674
+ const managed = instruction !== null && instruction.includes(MARKER_END)
675
+ && (instruction.includes(MARKER_START) || (state.predecessor && instruction.includes(PREDECESSOR_MARKER_START)));
676
+ // A kept, person-edited file (IC-007) is owned as it is now, markers or not: an edit can drop the
677
+ // span, and without its row the next run would see no owner and append the section unasked.
678
+ const kept = !removing && state.retained && instruction !== null;
679
+ if (instructionFile) {
522
680
  // Removal verification asks "did our section go away?"; install/update asks "is it managed?".
523
681
  statuses.push({
524
682
  assetId: POINTER_ASSET_ID, capability: 'instructions',
525
- status: removing ? (managed ? 'retained' : 'absent') : (managed ? 'managed' : 'missing'),
526
- target: paths.instruction,
683
+ status: removing ? (managed ? 'retained' : 'absent') : (managed ? 'managed' : (kept ? 'retained' : 'missing')),
684
+ target: instructionFile,
527
685
  });
528
- if (managed && !removing) {
529
- resources.push({
530
- assetId: POINTER_ASSET_ID, target: paths.instruction,
531
- ownershipIdentity: `doflow:${HARNESS}:instructions:managed-section`,
532
- kind: 'instructions-section', identity: 'AGENTS.md',
686
+ if (!removing && (managed || kept)) {
687
+ // Recorded with the file as it is now; a kept one is flagged so later runs keep it until --force.
688
+ resources.push(instructionsChange(instructionFile, {
533
689
  fingerprint: fingerprint(instruction), sourceVersion: 'registry-v1',
534
- projection: { renderer: 'antigravity-instructions' },
535
- });
690
+ ...(state.retained ? { retainedEdit: true } : {}),
691
+ }));
536
692
  }
537
693
  }
538
694
 
@@ -540,7 +696,7 @@ function verify(options = {}, impl = {}) {
540
696
  const destDir = treeDestFor(asset, paths, scope);
541
697
  if (!destDir) continue;
542
698
  const sourceDir = sourceDirFor(asset, { repoRoot: context.repoRoot }, fsImpl, HARNESS);
543
- const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, plannedWrites: context.plannedWrites });
699
+ const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.transform, select: asset.select, plannedWrites: context.plannedWrites });
544
700
  conflictsToStatuses(result.conflicts, asset.id, statuses);
545
701
  for (const resource of result.resources) {
546
702
  resources.push({
@@ -563,54 +719,10 @@ function verify(options = {}, impl = {}) {
563
719
  resources.push(...mcp.resources);
564
720
 
565
721
  // Hooks projection (project scope): the shims' bytes and the registered groups.
566
- if (scope === 'project') {
567
- const hooksTarget = paths.hooksJson;
568
- const scriptTargets = {
569
- 'pre-implementation-gate.sh': paths.hookScript,
570
- 'stop-check.sh': paths.stopHookScript,
571
- };
572
- // Source-side residue: reads the shims from THIS checkout (the authored source of the
573
- // projection, not an install destination — the destination is declared as the paths above).
574
- const scriptSourceDir = path.join(path.resolve(registryRepoRoot(options)), 'core', 'harnesses', 'antigravity', 'hooks');
575
- const removingOp = (context.operation ?? options.operation ?? '') === 'remove';
576
- const fingerprints = {};
577
- let scriptsOk = true;
578
- let anyPresent = false;
579
- for (const identity of HOOK_SCRIPTS) {
580
- let sourceFp = null;
581
- try { sourceFp = sha256File(fsImpl, path.join(scriptSourceDir, identity)); } catch { /* checkout without that shim */ }
582
- fingerprints[identity] = sourceFp;
583
- const target = scriptTargets[identity];
584
- const current = fsImpl.existsSync(target) ? sha256File(fsImpl, target) : null;
585
- if (current !== null) anyPresent = true;
586
- if (current === null || current !== sourceFp) scriptsOk = false;
587
- }
588
- const doc = readJsonObject(hooksTarget, { fsImpl });
589
- const gate = doc?.[HOOKS_GROUP];
590
- const stop = doc?.[STOP_HOOKS_GROUP];
591
- const gateOk = Boolean(gate?.PreToolUse?.[0]?.hooks?.[0]?.command)
592
- && gate.PreToolUse[0].hooks[0].command === scriptTargets['pre-implementation-gate.sh']
593
- && gate.PreToolUse[0].matcher === GATE_MATCHER;
594
- // Stop registration is matcher-free: handlers sit directly under the event key.
595
- const stopOk = Array.isArray(stop?.Stop) && stop.Stop.length > 0
596
- && stop.Stop[0]?.command === scriptTargets['stop-check.sh'];
597
- const groupsOk = gateOk && stopOk;
598
- statuses.push({ assetId: HOOKS_ASSET_ID, capability: 'hooks', status:
599
- removingOp ? (!anyPresent && !groupsOk ? 'absent' : 'retained')
600
- : (scriptsOk && groupsOk ? 'managed' : (!anyPresent && !groupsOk ? 'absent' : 'missing')),
601
- ownershipIdentity: `${HARNESS}:hooks:registration`, target: hooksTarget });
602
- if (!removingOp && scriptsOk && groupsOk) {
603
- resources.push({ assetId: HOOKS_ASSET_ID, target: hooksTarget,
604
- ownershipIdentity: `${HARNESS}:hooks:registration`, kind: 'hooks-json',
605
- fingerprint: fingerprint(JSON.stringify(doc)), sourceVersion: 'registry-v1',
606
- projection: { renderer: 'antigravity-hooks' } });
607
- for (const identity of HOOK_SCRIPTS) {
608
- resources.push({ assetId: HOOKS_ASSET_ID, target: scriptTargets[identity],
609
- ownershipIdentity: `${HARNESS}:copy-tree:${HOOKS_ASSET_ID}:${identity}`,
610
- kind: 'copy-tree-file', identity, fingerprint: fingerprints[identity],
611
- sourceVersion: 'registry-v1', projection: { renderer: 'antigravity-hooks' } });
612
- }
613
- }
722
+ if (scope === 'project' && context.hooksOff !== true) {
723
+ const hooks = verifyHooks({ options, paths, context, fsImpl });
724
+ statuses.push(...hooks.statuses);
725
+ resources.push(...hooks.resources);
614
726
  }
615
727
 
616
728
  const conflicts = [...statuses.filter((s) => s.status === 'conflict').map((s) => s.reason ?? s.identity), ...mcp.conflicts];
@@ -627,5 +739,6 @@ function conflictsToStatuses(conflicts , assetId , statuses ) {
627
739
 
628
740
  module.exports = {
629
741
  HARNESS, nativePaths: singleton.nativePaths, discover, render, plan, apply, remove, verify,
742
+ planHookDoors: singleton.planHookDoors, verifyHookDoors: singleton.verifyHookDoors,
630
743
  createAntigravityAdapter,
631
744
  };