@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,318 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ /**
6
+ * Retirement sweep (row 127 IC-003, design D2): release, delete or strip what a retired harness
7
+ * installed, driven only by the scope's ledger and the retired record in the registry. The retired
8
+ * harness has no adapter any more, so nothing here calls one.
9
+ *
10
+ * Every retired row gets exactly one change, first matching rule wins:
11
+ * 1. a live change in this run removes or supersedes its target -> drop-row
12
+ * 2. a non-retired ledger row has the same claim key -> release (file stays)
13
+ * 3. kind copy-tree-file | hooks-script: absent -> drop-row; bytes equal the row's fingerprint and the
14
+ * path resolves to itself -> delete; otherwise keep (named)
15
+ * 3a. a `removal.handover` entry (renderer + scope) at the replacement's own declared path: replacement
16
+ * planned in this run -> handover (it rewrites the span itself); installed but not planned -> defer
17
+ * (row and file stay, notice); otherwise on to rule 4. Decided by planHandovers before any live plan.
18
+ * 4. renderer in `markerSectionRenderers` -> strip-section (either start marker, see START_MARKERS); no
19
+ * markers -> drop-row; malformed -> keep
20
+ * 5. renderer = `hookCommands.renderer` -> strip-hooks (exact frozen commands only); unparseable -> keep;
21
+ * no matching command -> drop-row
22
+ * 6. anything else -> keep, `unknown row shape <kind>/<renderer>`
23
+ * Every action except `defer` gives the row up (retiredReleaseKeys). Actions that touch no file carry
24
+ * `release: true`, which keeps them out of the backup set like any other release; a handed-over file
25
+ * is backed up through the replacement's own change, which is the one write it gets.
26
+ */
27
+ const fs = require('node:fs');
28
+ const path = require('node:path');
29
+ const crypto = require('node:crypto');
30
+ const { isRetired } = require('../registry/index.js');
31
+ const { resolveHarnessPaths } = require('../helper/harness-paths.js');
32
+ const { ownershipKey, atomicJsonWrite } = require('../state/index.js');
33
+ const { removeTree, readJson } = require('../adapters/copy-tree.js');
34
+ const { removeMarkedSection, markedSpan, MARKER_START } = require('../helper/marker-merge.js');
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+ const WHOLE_FILE_KINDS = new Set(['copy-tree-file', 'hooks-script']);
56
+ /** Actions with no filesystem effect: the row is given up and nothing is written or deleted. */
57
+ const NO_FS_EFFECT = new Set (['release', 'handover', 'defer', 'drop-row', 'keep']);
58
+ /** The count each action adds to; every action has one, so the counts sum to the rows swept. */
59
+ const COUNTED = {
60
+ delete: 'deleted', 'strip-section': 'stripped', 'strip-hooks': 'stripped', release: 'released', handover: 'released',
61
+ 'drop-row': 'dropped', defer: 'deferred', keep: 'kept',
62
+ };
63
+
64
+ /** What one claim covers: a whole file, except an MCP entry, which is claimed on its own because
65
+ * several harnesses keep their servers side by side in one MCP file. The one definition: the
66
+ * lifecycle (src/lifecycle/index.js) imports it from here, since it requires this module. */
67
+ function claimKey(record ) {
68
+ return record.kind === 'mcp-server' ? `${record.target}\u0000${record.identity}` : record.target;
69
+ }
70
+
71
+ function sha256(bytes ) { return crypto.createHash('sha256').update(bytes).digest('hex'); }
72
+
73
+ /** Whether `target` is inside `scopeRoot` and resolves to that same place: no symlink between the
74
+ * scope root and the file (the file itself included) leads somewhere else. */
75
+ function resolvesInPlace(target , scopeRoot , fsImpl ) {
76
+ const rel = path.relative(scopeRoot, target);
77
+ if (!rel || rel === '..' || rel.startsWith(`..${path.sep}`) || path.isAbsolute(rel)) return false;
78
+ try { return fsImpl.realpathSync(target) === path.join(fsImpl.realpathSync(scopeRoot), rel); } catch { return false; }
79
+ }
80
+
81
+ /** The `hooks` value with every handler whose command is in `commands` taken out, and any matcher
82
+ * group, event array or the key itself that this left empty. Containers that were already empty,
83
+ * and every handler not in the list, stay as they are. Returns the new document, or null when no
84
+ * handler matched. */
85
+ function stripHookCommands(doc , key , commands ) {
86
+ const hooks = doc[key];
87
+ let removed = false;
88
+ const nextHooks = {};
89
+ for (const [event, groups] of Object.entries(hooks)) {
90
+ if (!Array.isArray(groups)) { nextHooks[event] = groups; continue; }
91
+ let eventChanged = false;
92
+ const nextGroups = [];
93
+ for (const group of groups) {
94
+ if (!group || !Array.isArray(group.hooks)) { nextGroups.push(group); continue; }
95
+ const handlers = group.hooks.filter((handler ) => !commands.has(handler?.command));
96
+ if (handlers.length === group.hooks.length) { nextGroups.push(group); continue; }
97
+ eventChanged = true;
98
+ if (handlers.length) nextGroups.push({ ...group, hooks: handlers });
99
+ }
100
+ if (eventChanged) removed = true;
101
+ if (!eventChanged || nextGroups.length) nextHooks[event] = eventChanged ? nextGroups : groups;
102
+ }
103
+ if (!removed) return null;
104
+ const next = { ...doc };
105
+ if (Object.keys(nextHooks).length) next[key] = nextHooks;
106
+ else delete next[key];
107
+ return next;
108
+ }
109
+
110
+ /** The hooks JSON at `target` parsed, or a reason it cannot be stripped. Absent file -> `absent`. */
111
+ function readHooksDoc(target , key , fsImpl ) {
112
+ const { exists, value, error } = readJson(target, { fsImpl });
113
+ if (!exists) return { absent: true };
114
+ if (error) return { error: 'not valid JSON' };
115
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return { error: 'not a JSON object' };
116
+ if (value[key] !== undefined && (!value[key] || typeof value[key] !== 'object' || Array.isArray(value[key]))) return { error: `'${key}' is not an object` };
117
+ return { doc: value };
118
+ }
119
+
120
+ /** The start markers a retired section may open with: marker-merge's, and the short one the retired
121
+ * gemini adapter wrote (src/adapters/gemini/index.ts @ c35f681b), which its real sections carry. Both
122
+ * close with MARKER_END. Only this sweep reads the short one; live harnesses never see it. */
123
+ const START_MARKERS = [MARKER_START, '<!-- doflow:start -->'];
124
+
125
+ /** Marker state of a file, by the rules removeMarkedSection applies with START_MARKERS. */
126
+ function markerState(text ) { return markedSpan(text, START_MARKERS).state; }
127
+
128
+ /** Rule 3a's verdict for one retired row (IC-003, IC-007, design D7). */
129
+
130
+
131
+ /**
132
+ * Rule 3a, decided before the live harnesses plan, because the replacement needs the verdict to plan
133
+ * its own change: `context.handovers` for each `to`. A retired row qualifies when a `removal.handover`
134
+ * entry names its renderer and this scope, its file is still there in place, and the file is the one
135
+ * the replacement itself declares for that surface here. Then: `to` planned in this run -> handover,
136
+ * `edited` when the whole file no longer has the bytes the row recorded; `to` installed in the scope
137
+ * (a ledger row of it, or its `targets` entry; a lock pins only harnesses the ledger holds, IC-004)
138
+ * -> defer. Rules 2 and 3 come first, as in classify. Returns the decisions by ownership key.
139
+ */
140
+ function planHandovers({ registry, ledger, scope, scopeRoot, planned = [], fsImpl = fs } ) {
141
+ const decisions = new Map ();
142
+ const retiredRecords = Array.isArray(registry?.retired) ? registry.retired : [];
143
+ if (!retiredRecords.length) return decisions;
144
+ const rows = (ledger?.resources ?? []).filter((row ) => typeof row?.target === 'string' && typeof row?.harness === 'string');
145
+ const liveClaims = new Set (rows.filter((row ) => !isRetired(registry, row.harness)).map(claimKey));
146
+ const plannedIds = new Set (planned);
147
+ for (const retired of retiredRecords) {
148
+ const entries = (retired.removal?.handover ?? []).filter((entry ) => entry?.scope === scope);
149
+ if (!entries.length) continue;
150
+ for (const row of rows.filter((item ) => item.harness === retired.id)) {
151
+ const entry = entries.find((item ) => item.renderer === row.projection?.renderer);
152
+ if (!entry || liveClaims.has(claimKey(row)) || WHOLE_FILE_KINDS.has(row.kind)) continue;
153
+ const to = registry.harnesses?.find((harness ) => harness.id === entry.to);
154
+ if (!to?.paths?.[entry.surface]) continue;
155
+ let declared = null;
156
+ try { declared = resolveHarnessPaths({ [entry.surface]: to.paths[entry.surface] }, { scope, scopeRoot, homeDir: scopeRoot })[entry.surface]; } catch { continue; }
157
+ if (declared !== row.target || !fsImpl.existsSync(row.target) || !resolvesInPlace(row.target, scopeRoot, fsImpl)) continue;
158
+ const base = { to: to.id, target: row.target, fromKey: ownershipKey(row) };
159
+ if (plannedIds.has(to.id)) {
160
+ decisions.set(base.fromKey, { action: 'handover', ...base, edited: sha256(fsImpl.readFileSync(row.target)) !== row.fingerprint });
161
+ } else if (rows.some((item ) => item.harness === to.id) || ledger?.targets?.[to.id]) {
162
+ decisions.set(base.fromKey, { action: 'defer', ...base, edited: false });
163
+ }
164
+ }
165
+ }
166
+ return decisions;
167
+ }
168
+
169
+ /** What the replacement is told about each handover in this run: `context.handovers` per harness id. */
170
+ function handoversFor(decisions ) {
171
+ const byHarness = new Map ();
172
+ for (const { action, to, target, fromKey, edited } of decisions.values()) {
173
+ if (action !== 'handover') continue;
174
+ if (!byHarness.has(to)) byHarness.set(to, []);
175
+ byHarness.get(to) .push({ target, fromKey, edited });
176
+ }
177
+ return byHarness;
178
+ }
179
+
180
+ /** Rule 3a in classify: the verdict planHandovers reached for this row before the live plans. */
181
+ function handoverAction(row , _retired , context ) {
182
+ const decided = context.handovers?.get(ownershipKey(row));
183
+ return decided ? { action: decided.action } : null;
184
+ }
185
+
186
+ function classify(row , retired , context ) {
187
+ const { liveRemoved, superseded, liveClaims, scopeRoot, fsImpl } = context;
188
+ const key = claimKey(row);
189
+ const renderer = row.projection?.renderer;
190
+ // 1. A live change in this run already releases this row (a removal, or a prune naming it).
191
+ if (liveRemoved.has(key) || superseded.has(ownershipKey(row))) return { action: 'drop-row' };
192
+ // 2. A live harness still claims the file.
193
+ if (liveClaims.has(key)) return { action: 'release' };
194
+ // 3. Whole files DoFlow wrote: delete only its own unchanged bytes, at the path it recorded.
195
+ if (WHOLE_FILE_KINDS.has(row.kind)) {
196
+ if (!fsImpl.existsSync(row.target)) return { action: 'drop-row' };
197
+ if (!resolvesInPlace(row.target, scopeRoot, fsImpl)) return { action: 'keep', reason: 'resolves outside its recorded path' };
198
+ if (!fsImpl.statSync(row.target).isFile()) return { action: 'keep', reason: 'not a regular file' };
199
+ const current = sha256(fsImpl.readFileSync(row.target));
200
+ if (current !== row.fingerprint) return { action: 'keep', reason: 'changed since DoFlow wrote it' };
201
+ return { action: 'delete', fingerprint: current };
202
+ }
203
+ // 3a. Handover to the replacement (T-127-A5).
204
+ const handover = handoverAction(row, retired, context);
205
+ if (handover) return handover;
206
+ // 4. A marker section in a file the person also writes.
207
+ if (retired.removal.markerSectionRenderers.includes(renderer)) {
208
+ if (!fsImpl.existsSync(row.target)) return { action: 'drop-row' };
209
+ if (!resolvesInPlace(row.target, scopeRoot, fsImpl)) return { action: 'keep', reason: 'resolves outside its recorded path' };
210
+ const state = markerState(fsImpl.readFileSync(row.target, 'utf8'));
211
+ if (state === 'none') return { action: 'drop-row' };
212
+ if (state === 'malformed') return { action: 'keep', reason: 'malformed DoFlow markers' };
213
+ return { action: 'strip-section' };
214
+ }
215
+ // 5. Hook handlers in a shared JSON file: only the exact frozen commands.
216
+ const hookCommands = retired.removal.hookCommands;
217
+ if (renderer === hookCommands.renderer) {
218
+ const read = readHooksDoc(row.target, hookCommands.key, fsImpl);
219
+ if (read.absent) return { action: 'drop-row' };
220
+ if (read.error) return { action: 'keep', reason: read.error };
221
+ if (!resolvesInPlace(row.target, scopeRoot, fsImpl)) return { action: 'keep', reason: 'resolves outside its recorded path' };
222
+ if (read.doc[hookCommands.key] === undefined) return { action: 'drop-row' };
223
+ return stripHookCommands(read.doc, hookCommands.key, new Set(hookCommands.commands)) ? { action: 'strip-hooks' } : { action: 'drop-row' };
224
+ }
225
+ // 6. Nothing this sweep knows how to take back.
226
+ return { action: 'keep', reason: `unknown row shape ${row.kind ?? '(none)'}/${renderer ?? '(none)'}` };
227
+ }
228
+
229
+ /** One plan per retired harness that still has rows in this scope's ledger, in registry order. A ledger
230
+ * with no retired rows yields []. `livePlanChanges` are the live harnesses' planned changes, and
231
+ * `handovers` planHandovers' decisions for this same run. */
232
+ function planRetiredHarnesses({ registry, ledger, scope, scopeRoot, livePlanChanges = [], handovers = new Map(), fsImpl = fs } ) {
233
+ const retiredRecords = Array.isArray(registry?.retired) ? registry.retired : [];
234
+ if (!retiredRecords.length) return [];
235
+ const rows = (ledger?.resources ?? []).filter((row ) => typeof row?.target === 'string' && typeof row?.harness === 'string');
236
+ const liveRemoved = new Set ();
237
+ const superseded = new Set ();
238
+ for (const change of livePlanChanges) {
239
+ if (change?.operation !== 'remove' || typeof change.target !== 'string') continue;
240
+ liveRemoved.add(claimKey(change));
241
+ for (const key of change.supersedes ?? []) superseded.add(key);
242
+ }
243
+ const liveClaims = new Set (rows.filter((row ) => !isRetired(registry, row.harness)).map(claimKey));
244
+ const context = { registry, scope, scopeRoot, livePlanChanges, liveRemoved, superseded, liveClaims, handovers, fsImpl };
245
+
246
+ const plans = [];
247
+ for (const retired of retiredRecords) {
248
+ const own = rows.filter((row ) => row.harness === retired.id);
249
+ if (!own.length) continue;
250
+ const plan = {
251
+ harness: retired.id, replacement: retired.replacement, scopeRoot, changes: [],
252
+ counts: { deleted: 0, stripped: 0, released: 0, dropped: 0, deferred: 0, kept: 0 }, notices: [],
253
+ };
254
+ for (const row of own) {
255
+ const { action, reason, fingerprint } = classify(row, retired, context);
256
+ const change = {
257
+ harness: row.harness, retired: true, assetId: row.assetId, target: row.target,
258
+ ownershipKey: ownershipKey(row), ownershipIdentity: row.ownershipIdentity, action, operation: 'remove',
259
+ ...(reason ? { reason } : {}),
260
+ ...(NO_FS_EFFECT.has(action) ? { release: true } : {}),
261
+ ...(fingerprint ? { fingerprint } : {}),
262
+ ...(action === 'strip-hooks' ? { hookCommands: { key: retired.removal.hookCommands.key, commands: [...retired.removal.hookCommands.commands] } } : {}),
263
+ };
264
+ plan.changes.push(Object.freeze(change) );
265
+ plan.counts[COUNTED[action]] += 1;
266
+ if (action === 'keep') plan.notices.push(`kept ${path.relative(scopeRoot, row.target)}: ${reason}`);
267
+ if (action === 'defer') plan.notices.push(`${path.relative(scopeRoot, row.target)}: handed over on the next run that targets ${handovers.get(change.ownershipKey).to}`);
268
+ }
269
+ plans.push(plan);
270
+ }
271
+ return plans;
272
+ }
273
+
274
+ /** Carry out the plans: JSON hook strips first, then marker sections, then whole-file deletes, so no
275
+ * settings file is ever left naming a script that is already gone. Each step re-checks what the plan
276
+ * proved and throws rather than act on a file that changed since; the caller's recovery restores. */
277
+ function applyRetiredHarnesses(plans , { fsImpl = fs } = {}) {
278
+ const all = plans.flatMap((plan) => plan.changes.map((change) => ({ change, scopeRoot: plan.scopeRoot })));
279
+ const guard = (change , scopeRoot ) => {
280
+ if (fsImpl.existsSync(change.target) && !resolvesInPlace(change.target, scopeRoot, fsImpl)) {
281
+ throw new Error(`Refusing to change retired ${change.harness} resource ${change.target}: it resolves outside its recorded path`);
282
+ }
283
+ };
284
+ for (const { change, scopeRoot } of all) {
285
+ if (change.action !== 'strip-hooks' || !change.hookCommands) continue;
286
+ guard(change, scopeRoot);
287
+ const read = readHooksDoc(change.target, change.hookCommands.key, fsImpl);
288
+ if (read.absent) continue;
289
+ if (read.error) throw new Error(`Refusing to strip retired ${change.harness} hooks from ${change.target}: ${read.error}`);
290
+ if (read.doc[change.hookCommands.key] === undefined) continue;
291
+ const next = stripHookCommands(read.doc, change.hookCommands.key, new Set(change.hookCommands.commands));
292
+ if (next) atomicJsonWrite(change.target, next, { fsImpl });
293
+ }
294
+ for (const { change, scopeRoot } of all) {
295
+ if (change.action !== 'strip-section') continue;
296
+ guard(change, scopeRoot);
297
+ removeMarkedSection(change.target, { startMarkers: START_MARKERS });
298
+ }
299
+ const deletes = all.filter(({ change }) => change.action === 'delete');
300
+ for (const { change, scopeRoot } of deletes) guard(change, scopeRoot);
301
+ removeTree({ changes: deletes.map(({ change }) => ({ operation: 'remove', target: change.target, fingerprint: change.fingerprint, relPath: change.target })), fsImpl });
302
+ }
303
+
304
+ /** Every ledger row the plans give up: all of them except deferred ones. */
305
+ function retiredReleaseKeys(plans ) {
306
+ return new Set(plans.flatMap((plan) => plan.changes.filter((change) => change.action !== 'defer').map((change) => change.ownershipKey)));
307
+ }
308
+
309
+ /** IC-003 output, for dry-run and apply alike: one summary line per retired harness, whose six counts
310
+ * sum to the rows it swept, then one line per kept or named item. No plans, no lines. */
311
+ function formatRetiredPlans(plans ) {
312
+ return plans.flatMap((plan) => [
313
+ `[RETIRED] ${plan.harness} → ${plan.replacement}: deleted ${plan.counts.deleted}, stripped ${plan.counts.stripped}, released ${plan.counts.released}, dropped ${plan.counts.dropped}, deferred ${plan.counts.deferred}, kept ${plan.counts.kept}`,
314
+ ...plan.notices.map((notice) => ` ${notice}`),
315
+ ]);
316
+ }
317
+
318
+ module.exports = { planRetiredHarnesses, planHandovers, handoversFor, applyRetiredHarnesses, retiredReleaseKeys, formatRetiredPlans, claimKey };
@@ -2,7 +2,7 @@
2
2
 
3
3
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
4
  // lifecycle-view.js — CLI-facing view over the registry/lifecycle path: computes a plan across
5
- // every requested harness (claude/codex/gemini) from a loaded registry, a neutral ledger, and the
5
+ // every requested harness (claude, codex, opencode, pi, copilot, kiro, antigravity) from a loaded registry, a neutral ledger, and the
6
6
  // Codex-native source locations under `repoRoot`, then renders/gates it for bin/doflow.js's
7
7
  // install/update/remove/status commands.
8
8
  const fs = require('node:fs');
@@ -13,16 +13,17 @@ const { createAdapterRegistry } = require('../adapters/index.js');
13
13
  const { declaredHarnessPaths } = require('../helper/harness-paths.js');
14
14
  const claudeAdapter = require('../adapters/claude/index.js');
15
15
  const codexAdapter = require('../adapters/codex/index.js');
16
- const { createGeminiAdapter } = require('../adapters/gemini/index.js');
17
16
  const { createOpenCodeAdapter } = require('../adapters/opencode/index.js');
18
17
  const { createPiAdapter } = require('../adapters/pi/index.js');
19
18
  const { createCopilotAdapter } = require('../adapters/copilot/index.js');
20
19
  const { createKiroAdapter } = require('../adapters/kiro/index.js');
21
20
  const { createAntigravityAdapter } = require('../adapters/antigravity/index.js');
22
21
  const { planLifecycle } = require('./index.js');
22
+ const { formatRetiredPlans } = require('./retired-harnesses.js');
23
+ const { resolveHookTier, hookTierWarnings } = require('./hook-tier.js');
23
24
  const { stateRoot, readLedger, defaultLedger } = require('../state/index.js');
24
25
  const { selectAssets } = require('../registry/index.js');
25
- const { defaultLock, readLock, writeLock, diffLocks, removeLock, pinnedSelections } = require('../state/lockfile.js');
26
+ const { defaultLock, readLock, writeLock, diffLocks, removeLock, pinnedSelections, lockPackages } = require('../state/lockfile.js');
26
27
  // Tolerant because the projected runtime under `.doflow/runtime/` ships bin/, src/ and
27
28
  // core/registry/ but no package.json — see the `runtime.*` assets in core/registry/assets.json.
28
29
  // A hard require here would make every Node-backed verb fail in an install, which is the exact
@@ -48,17 +49,18 @@ function codexConfigResources(repoRoot , fsImpl ) {
48
49
  * mutation path until every native adapter has CLI-level parity.
49
50
  * `registry` is loaded once per command (by the caller) and threaded through here rather than
50
51
  * reloaded — the same registry also resolves every harness's MCP selection for that command. */
51
- function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, mcpAdoptable = {}, retainedMcpIds = [], operation, repoRoot, force = false, adopt = false, permissions = false, statusline = false, fsImpl = fs } ) {
52
+ function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, mcpAdoptable = {}, retainedMcpIds = [], operation, repoRoot, force = false, adopt = false, permissions = false, statusline = false, hookHarnesses, fsImpl = fs } ) {
52
53
  const lifecycleScope = codexScope(scope);
53
54
  const scopeRoot = scope.global ? os.homedir() : path.resolve(scope.projectRoot);
54
55
  const neutralStateRoot = stateRoot({ scope: lifecycleScope, projectRoot: scopeRoot, homeDir: scopeRoot });
55
56
  const ledger = readLedger(neutralStateRoot) ?? defaultLedger({ scope: lifecycleScope, scopeRoot });
57
+ const tier = hookHarnesses ? { harnesses: hookHarnesses, workers: null } : resolveHookTier();
56
58
  // Mirrors buildAdapterRegistry() in src/cli/shared.js — including the declared-paths wiring —
57
59
  // and is kept as a literal construction here because the registry guard pins every declared
58
60
  // harness into each adapter-registry call site by parsing it.
59
61
  const declared = declaredHarnessPaths();
60
62
  const adapters = createAdapterRegistry({ claude: claudeAdapter.createClaudeAdapter({ declaredPaths: declared.claude }),
61
- codex: codexAdapter.createCodexAdapter({ declaredPaths: declared.codex }), gemini: createGeminiAdapter({ declaredPaths: declared.gemini }),
63
+ codex: codexAdapter.createCodexAdapter({ declaredPaths: declared.codex }),
62
64
  opencode: createOpenCodeAdapter({ declaredPaths: declared.opencode }), pi: createPiAdapter({ declaredPaths: declared.pi }),
63
65
  copilot: createCopilotAdapter({ declaredPaths: declared.copilot }), kiro: createKiroAdapter({ declaredPaths: declared.kiro }),
64
66
  antigravity: createAntigravityAdapter({ declaredPaths: declared.antigravity }) });
@@ -68,19 +70,21 @@ function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, m
68
70
  codexAgentsSourceDir: path.join(repoRoot, 'core', 'harnesses', 'codex', 'agents'),
69
71
  codexHooksSourceFile: path.join(repoRoot, 'core', 'harnesses', 'codex', 'hooks', 'hooks.json'),
70
72
  codexHooksSourceDir: path.join(repoRoot, 'core', 'harnesses', 'codex', 'hooks'),
71
- geminiHooksSourceFile: path.join(repoRoot, 'core', 'harnesses', 'gemini', 'hooks', 'hooks.json'),
72
- geminiHooksSourceDir: path.join(repoRoot, 'core', 'harnesses', 'gemini', 'hooks'),
73
73
  operation,
74
74
  force,
75
75
  adopt,
76
76
  permissions,
77
77
  statusline,
78
+ // maint-hooks: the person's hook tier (a caller may pass its own); every harness outside it gets content only.
79
+ hookHarnesses: tier.harnesses,
78
80
  } });
79
- return { registry, stateRoot: neutralStateRoot, ledger, plan, adapters };
81
+ const hookWarnings = operation === 'remove' ? [] : hookTierWarnings(registry, plan.targets.filter((t ) => !t.skipped).map((t ) => t.harness), tier);
82
+ return { registry, stateRoot: neutralStateRoot, ledger, plan, adapters, hookWarnings };
80
83
  }
81
84
 
82
85
  function printRegistryLifecycle(view , prefix = '[PLAN]') {
83
86
  console.log(`${prefix} Registry lifecycle: ${view.plan.changes.length} native change(s), ${view.plan.conflicts.length} conflict(s), ${view.plan.prerequisites.length} prerequisite(s)`);
87
+ for (const warning of view.hookWarnings ?? []) console.log(`[WARN] ${warning}`);
84
88
  for (const target of view.plan.targets) {
85
89
  console.log(`${prefix} ${target.harness}: ${target.changes.length} change(s)${target.conflicts.length ? `; conflicts: ${target.conflicts.join('; ')}` : ''}`);
86
90
  const hookChange = target.changes.find((change ) => change.nativeComponent === 'hooks');
@@ -89,19 +93,29 @@ function printRegistryLifecycle(view , prefix = '[PLAN]') {
89
93
  }
90
94
  for (const notice of target.notices ?? []) console.log(`${prefix} ${target.harness}: ${notice}`);
91
95
  }
96
+ printRetiredPlans(view);
92
97
  console.log(`${prefix} Neutral state: ${view.stateRoot}${readLedger(view.stateRoot) ? ' (existing ledger)' : ' (not yet created)'}`);
93
98
  }
94
99
 
100
+ /** The retirement lines (row 127 IC-003), the same under --dry-run and after an apply: one
101
+ * `[RETIRED] <id> → <replacement>: deleted N, stripped N, released N, dropped N, deferred N, kept N`
102
+ * summary per retired harness the plan sweeps, then one line per kept or deferred item. Nothing
103
+ * when the ledger holds no retired row. */
104
+ function printRetiredPlans(view ) {
105
+ for (const line of formatRetiredPlans(view.plan.retiredHarnesses ?? [])) console.log(line);
106
+ }
107
+
95
108
  /** Prints the plan's notices after a real run. Independent of whether anything changed: a no-op
96
109
  * reinstall of a harness that has a gap must still say so. */
97
110
  function printPlanNotices(view ) {
111
+ for (const warning of view.hookWarnings ?? []) console.log(`[WARN] ${warning}`);
98
112
  for (const { harness, notice } of view.plan.notices) console.log(`[INFO] ${harness}: ${notice}`);
99
113
  }
100
114
 
101
115
  /** Harnesses whose native resources are reconciled through the registry/lifecycle path (all of
102
116
  * them, as of this wiring). Kept as an explicit list — rather than reusing VALID from
103
117
  * src/targets.js — so a future non-lifecycle target doesn't silently gain lifecycle behavior. */
104
- const LIFECYCLE_HARNESSES = ['claude', 'codex', 'gemini', 'opencode', 'pi', 'copilot', 'kiro', 'antigravity'];
118
+ const LIFECYCLE_HARNESSES = ['claude', 'codex', 'opencode', 'pi', 'copilot', 'kiro', 'antigravity'];
105
119
 
106
120
  function assertSafeRegistryPlan(view ) {
107
121
  if (view.plan.safe) return;
@@ -117,13 +131,18 @@ function assertSafeRegistryPlan(view ) {
117
131
  * as the ledger (as the run left it) still holds a resource of that harness, so a narrower later run
118
132
  * never drops another harness's pin and a harness whose last resource is gone leaves it. MCP
119
133
  * selections carry over for pinned harnesses and are replaced by the rows this run resolved, `[]`
120
- * included. Assets are recomputed from the registry for every pinned harness. `null` when no harness
121
- * stays pinned. */
134
+ * included. Assets are recomputed from the registry for every pinned harness. The previous lock's
135
+ * `packages` (122 IC-007) are not registry-derived, so they are carried forward unchanged, and a lock
136
+ * that holds packages stays as `targets: []` once no harness is pinned. `null` when no harness stays
137
+ * pinned and no package is recorded. Only live harnesses are pinned (row 127 IC-004): a previous pin
138
+ * of a retired id is dropped, even while the ledger still holds its rows. */
122
139
  function lockDocument({ registry, scope, scopeRoot, previous = null, ledger, plannedTargets = [], mcpSelections = {}, sourceVersion = pkg.version, now = new Date() } ) {
123
140
  const held = new Set((ledger?.resources ?? []).map((resource ) => resource?.harness));
141
+ const live = new Set(registry.harnesses.map((harness ) => harness.id));
124
142
  const pinned = [...new Set([...(previous?.targets ?? []).map((entry ) => entry.harness), ...plannedTargets])]
125
- .filter((harness) => held.has(harness)).sort();
126
- if (!pinned.length) return null;
143
+ .filter((harness) => held.has(harness) && live.has(harness)).sort();
144
+ const packages = lockPackages(previous);
145
+ if (!pinned.length && !packages.length) return null;
127
146
  const assets = [];
128
147
  for (const harness of pinned) {
129
148
  for (const asset of selectAssets(registry, { harness })) {
@@ -139,6 +158,7 @@ function lockDocument({ registry, scope, scopeRoot, previous = null, ledger, pla
139
158
  targets: pinned.map((harness) => ({ harness })),
140
159
  assets,
141
160
  mcpSelections: Object.fromEntries(pinned.filter((harness) => Array.isArray(rows[harness])).map((harness) => [harness, rows[harness]])),
161
+ ...(packages.length ? { packages } : {}),
142
162
  };
143
163
  }
144
164
 
@@ -150,7 +170,7 @@ function lockDelta(previous , next ) {
150
170
  if (!previous) return { changed: true, summary: 'created' };
151
171
  const diff = diffLocks(previous, next);
152
172
  if (diff.clean) return { changed: false, summary: 'unchanged' };
153
- const count = ['targets', 'assets']
173
+ const count = ['targets', 'assets', 'packages']
154
174
  .flatMap((section) => Object.values (diff[section]).map((list ) => list.length))
155
175
  .reduce((sum, n) => sum + n, 0)
156
176
  + diff.mcpSelections.changed.length + (diff.meta.sourceVersion ? 1 : 0);
@@ -170,6 +190,6 @@ function recordLock(scopeArgs , next , { fsImpl = fs } = {}) {
170
190
  }
171
191
 
172
192
  module.exports = {
173
- codexScope, registryLifecycleView, printRegistryLifecycle, printPlanNotices, LIFECYCLE_HARNESSES, assertSafeRegistryPlan,
193
+ codexScope, registryLifecycleView, printRegistryLifecycle, printRetiredPlans, printPlanNotices, LIFECYCLE_HARNESSES, assertSafeRegistryPlan,
174
194
  lockDocument, lockDelta, recordLock,
175
195
  };