@khoavu882/doflow 2.0.0-beta.5 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (494) hide show
  1. package/README.md +16 -16
  2. package/bin/doflow.js +22 -12
  3. package/core/.antigravity-plugin/plugin.json +1 -2
  4. package/core/.claude-plugin/plugin.json +1 -1
  5. package/core/.codex-plugin/plugin.json +1 -1
  6. package/core/.plugin/plugin.json +1 -1
  7. package/core/harnesses/claude/hooks/lib.sh +43 -24
  8. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +4 -1
  9. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  10. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  11. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  12. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  13. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  14. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  15. package/core/harnesses/claude/hooks/pre-implement-gate.sh +4 -1
  16. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  17. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  18. package/core/harnesses/claude/settings/settings.json +26 -9
  19. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  20. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  21. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  22. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  23. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  24. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  25. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  26. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  27. package/core/harnesses/codex/hooks/hooks.json +0 -10
  28. package/core/harnesses/codex/hooks/lib.sh +44 -25
  29. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +4 -1
  30. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  31. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  32. package/core/harnesses/codex/hooks/pre-implement-gate.sh +4 -1
  33. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  34. package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
  35. package/core/harnesses/copilot/hooks/doflow-lib.sh +94 -0
  36. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  37. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  38. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  39. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  40. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  41. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  42. package/core/harnesses/copilot/hooks/doflow.json +51 -0
  43. package/core/harnesses/kiro/agents/doflow.json +1 -11
  44. package/core/harnesses/kiro/hooks/doflow.json +0 -10
  45. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  46. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +8 -3
  47. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +8 -3
  48. package/core/harnesses/kiro/hooks/session-start.sh +1 -0
  49. package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
  50. package/core/harnesses/pi/extensions/doflow-guard.js +11 -2
  51. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +1 -2
  52. package/core/harnesses/shared/hooks/adapters/antigravity.js +1 -1
  53. package/core/harnesses/shared/hooks/bridge/bridge.sh +126 -23
  54. package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
  55. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  56. package/core/harnesses/shared/hooks/bridge/respond.sh +16 -2
  57. package/core/harnesses/shared/hooks/policies/capture-failure.sh +49 -16
  58. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  59. package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
  60. package/core/harnesses/shared/hooks/policies/deny-json.sh +20 -7
  61. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  62. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  63. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  64. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  65. package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
  66. package/core/harnesses/shared/hooks/policies/mcp-policy.conf +1 -1
  67. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +7 -8
  68. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +132 -57
  69. package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
  70. package/core/harnesses/shared/hooks/policies/stop-check.sh +27 -15
  71. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +6 -3
  72. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  73. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
  74. package/core/harnesses/shared/hooks/stream-hook-runner.js +58 -20
  75. package/core/harnesses/shared/locator/doflow-run +2 -3
  76. package/core/registry/advice.json +3 -3
  77. package/core/registry/assets.json +55 -82
  78. package/core/registry/capabilities.json +12 -0
  79. package/core/registry/contracts.json +58 -31
  80. package/core/registry/contribution-points.json +35 -0
  81. package/core/registry/gates.json +32 -0
  82. package/core/registry/guidance-caps.json +40 -0
  83. package/core/registry/harnesses.json +1445 -366
  84. package/core/registry/intake.json +53 -0
  85. package/core/registry/lifecycle.json +599 -996
  86. package/core/registry/mcp.json +5 -1
  87. package/core/registry/models.json +7 -1
  88. package/core/registry/package-templates/README.md +19 -0
  89. package/core/registry/package-templates/agent/files/agent.md +9 -0
  90. package/core/registry/package-templates/agent/template.json +4 -0
  91. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  92. package/core/registry/package-templates/capability-provider/template.json +11 -0
  93. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  94. package/core/registry/package-templates/debate-preset/template.json +4 -0
  95. package/core/registry/package-templates/intake-category/files/category.json +8 -0
  96. package/core/registry/package-templates/intake-category/template.json +4 -0
  97. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  98. package/core/registry/package-templates/review-preset/template.json +4 -0
  99. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  100. package/core/registry/package-templates/skill/template.json +4 -0
  101. package/core/registry/package-templates/worker/files/worker.json +92 -0
  102. package/core/registry/package-templates/worker/template.json +11 -0
  103. package/core/registry/packages/doflow/doflow-package.json +15 -0
  104. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  105. package/core/registry/protocols.json +61 -0
  106. package/core/registry/routes.json +17 -0
  107. package/core/registry/rules.json +431 -0
  108. package/core/registry/settings.json +272 -0
  109. package/core/registry/storage.json +687 -31
  110. package/core/registry/telemetry.json +236 -0
  111. package/core/registry/transcripts.json +142 -0
  112. package/core/registry/verification.json +76 -6
  113. package/core/registry/workflows.json +55 -0
  114. package/core/shared/agent-specs/core-implementer.md +3 -0
  115. package/core/shared/agent-specs/delivery-lead.md +68 -0
  116. package/core/shared/agent-specs/quality-guardian.md +5 -1
  117. package/core/shared/agent-specs/research-writer.md +10 -3
  118. package/core/shared/agent-specs/reviewer.md +53 -0
  119. package/core/shared/agent-specs/spec-analyst.md +5 -6
  120. package/core/shared/agent-specs/system-architect.md +5 -1
  121. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  122. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  123. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  124. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  125. package/core/shared/guidance/pointers/codex.md +1 -4
  126. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  127. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  128. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  129. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  130. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  131. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  132. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  133. package/core/shared/guidance/references/ORCHESTRATION.md +3 -2
  134. package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +19 -1
  135. package/core/shared/guidance/rules/RULES.md +45 -0
  136. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  137. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  138. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  139. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  140. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  141. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  142. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  143. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  144. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  145. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  146. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  147. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +49 -5
  148. package/core/shared/scripts/doflow/bin/doflow-run +152 -34
  149. package/core/shared/skills/do/SKILL.md +14 -12
  150. package/core/shared/skills/do/references/advice_calls.md +26 -0
  151. package/core/shared/skills/do/references/pm_routing.md +1 -2
  152. package/core/shared/skills/do/references/program_mode.md +31 -0
  153. package/core/shared/skills/do-brainstorm/SKILL.md +45 -97
  154. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +36 -0
  155. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  156. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  157. package/core/shared/skills/do-code-review/SKILL.md +48 -148
  158. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  159. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  160. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  161. package/core/shared/skills/do-design/SKILL.md +23 -20
  162. package/core/shared/skills/do-diagnose/SKILL.md +18 -6
  163. package/core/shared/skills/do-document/SKILL.md +12 -1
  164. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  165. package/core/shared/skills/do-document/references/embedded-research.md +19 -7
  166. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  167. package/core/shared/skills/do-execute-plan/SKILL.md +19 -60
  168. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  169. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  170. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
  171. package/core/shared/skills/do-execute-plan/references/run_position.md +16 -2
  172. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  173. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  174. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  175. package/core/shared/skills/do-flow/SKILL.md +10 -11
  176. package/core/shared/skills/do-flow/references/task_classes.md +5 -5
  177. package/core/shared/skills/do-git/SKILL.md +4 -0
  178. package/core/shared/skills/do-implement/SKILL.md +13 -14
  179. package/core/shared/skills/do-plan/SKILL.md +34 -20
  180. package/core/shared/skills/do-test/SKILL.md +7 -4
  181. package/core/shared/templates/doflow/constitution-template.md +9 -3
  182. package/core/shared/templates/doflow/data-model-template.md +1 -1
  183. package/core/shared/templates/doflow/design-template.md +113 -16
  184. package/core/shared/templates/doflow/plan-template.md +11 -10
  185. package/core/shared/templates/doflow/question-log-template.md +14 -2
  186. package/core/shared/templates/doflow/requirement-template.md +1 -1
  187. package/core/shared/workflows/do-flow-chain.md +2 -0
  188. package/core/worker-evidence/antigravity/checks.json +14 -0
  189. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  190. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  191. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  192. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  193. package/core/worker-evidence/codex/checks.json +38 -0
  194. package/package.json +10 -6
  195. package/src/adapters/antigravity/index.js +143 -66
  196. package/src/adapters/claude/index.js +46 -5
  197. package/src/adapters/codex/agents.js +25 -15
  198. package/src/adapters/codex/hooks.js +39 -3
  199. package/src/adapters/codex/index.js +61 -13
  200. package/src/adapters/codex/native-projection.js +6 -2
  201. package/src/adapters/copilot/index.js +58 -23
  202. package/src/adapters/copy-tree.js +291 -46
  203. package/src/adapters/guidance-manifest.js +71 -0
  204. package/src/adapters/guidance-pointer.js +19 -0
  205. package/src/adapters/hook-commands.js +6 -10
  206. package/src/adapters/index.js +1 -0
  207. package/src/adapters/kiro/index.js +23 -12
  208. package/src/adapters/opencode/index.js +8 -8
  209. package/src/adapters/pi/hooks.js +2 -1
  210. package/src/adapters/pi/index.js +14 -7
  211. package/src/adapters/resolve-source.js +26 -2
  212. package/src/cli/commands/install.js +2 -1
  213. package/src/cli/commands/reconcile.js +53 -9
  214. package/src/cli/commands/remove.js +2 -2
  215. package/src/cli/commands/rollback.js +6 -3
  216. package/src/cli/commands/self-update.js +18 -9
  217. package/src/cli/commands/status.js +22 -3
  218. package/src/cli/commands/ui.js +99 -0
  219. package/src/cli/commands/update.js +2 -1
  220. package/src/cli/index.js +337 -32
  221. package/src/cli/runtime-commands.js +220 -60
  222. package/src/cli/shared.js +0 -2
  223. package/src/helper/git.js +1 -1
  224. package/src/helper/guardrails.js +1 -1
  225. package/src/helper/marker-merge.js +31 -13
  226. package/src/helper/package-ids.js +37 -0
  227. package/src/helper/semver.js +94 -0
  228. package/src/install/backup.js +105 -15
  229. package/src/install/context.js +1 -1
  230. package/src/install/targets.js +31 -16
  231. package/src/lifecycle/coverage.js +4 -16
  232. package/src/lifecycle/hook-tier.js +160 -0
  233. package/src/lifecycle/index.js +412 -78
  234. package/src/lifecycle/retired-harnesses.js +318 -0
  235. package/src/lifecycle/view.js +35 -15
  236. package/src/registry/index.js +551 -26
  237. package/src/registry/worker-ids.js +45 -0
  238. package/src/release/build-package.js +0 -3
  239. package/src/release/docs-blocks.js +472 -0
  240. package/src/release/preflight.js +425 -0
  241. package/src/release/rehearse.js +401 -0
  242. package/src/release/release-workflow.js +733 -0
  243. package/src/release/sync-plugin-versions.js +43 -7
  244. package/src/runtime/advice/bench-gate.js +25 -0
  245. package/src/runtime/advice/cli.js +59 -16
  246. package/src/runtime/advice/client.js +32 -9
  247. package/src/runtime/advice/config.js +98 -133
  248. package/src/runtime/advice/doctor.js +1 -1
  249. package/src/runtime/advice/elevation.js +97 -0
  250. package/src/runtime/advice/endpoint-guard.js +9 -3
  251. package/src/runtime/advice/mode.js +37 -0
  252. package/src/runtime/advice/record.js +18 -5
  253. package/src/runtime/advice/registry.js +4 -1
  254. package/src/runtime/advice/shadow-config.js +229 -0
  255. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  256. package/src/runtime/brief-rules.js +81 -0
  257. package/src/runtime/brief-tools.js +111 -34
  258. package/src/runtime/candidate.js +20 -11
  259. package/src/runtime/capabilities/executor.js +450 -0
  260. package/src/runtime/capabilities/loopback.js +41 -0
  261. package/src/runtime/capabilities/sanitizer.js +88 -0
  262. package/src/runtime/capability-router.js +135 -3
  263. package/src/runtime/checkouts.js +73 -11
  264. package/src/runtime/claims.js +13 -7
  265. package/src/runtime/cli.js +196 -19
  266. package/src/runtime/config-home.js +42 -0
  267. package/src/runtime/context/arbitrate.js +767 -0
  268. package/src/runtime/context/delivery-store.js +4 -6
  269. package/src/runtime/context/emission-rules.js +118 -0
  270. package/src/runtime/context/identity.js +227 -0
  271. package/src/runtime/context/pointer.js +67 -12
  272. package/src/runtime/context/primary-command.js +117 -0
  273. package/src/runtime/context/primary.js +344 -0
  274. package/src/runtime/context/summary-store.js +655 -0
  275. package/src/runtime/context-pack.js +158 -79
  276. package/src/runtime/decision-register.js +13 -4
  277. package/src/runtime/dispatch/admission.js +392 -0
  278. package/src/runtime/dispatch/attempt-end.js +274 -0
  279. package/src/runtime/dispatch/command.js +628 -0
  280. package/src/runtime/dispatch/controls.js +904 -0
  281. package/src/runtime/dispatch/envelope.js +276 -0
  282. package/src/runtime/dispatch/grants.js +310 -0
  283. package/src/runtime/dispatch/integrate.js +617 -0
  284. package/src/runtime/dispatch/loop.js +1835 -0
  285. package/src/runtime/dispatch/store.js +352 -0
  286. package/src/runtime/dispatch/verify-run.js +201 -0
  287. package/src/runtime/evidence-ledger.js +52 -7
  288. package/src/runtime/failure/capture.js +3 -1
  289. package/src/runtime/failure/cli.js +25 -9
  290. package/src/runtime/failure/home.js +57 -30
  291. package/src/runtime/failure/store.js +7 -2
  292. package/src/runtime/feature-pointer.js +174 -0
  293. package/src/runtime/feature-resolve.js +13 -2
  294. package/src/runtime/fleet/command.js +77 -0
  295. package/src/runtime/fleet/projects.js +178 -0
  296. package/src/runtime/fleet/tree.js +383 -0
  297. package/src/runtime/freshness.js +33 -3
  298. package/src/runtime/git-memo.js +46 -0
  299. package/src/runtime/guidance/ablation.js +427 -0
  300. package/src/runtime/health.js +624 -18
  301. package/src/runtime/history-compactor.js +13 -3
  302. package/src/runtime/implementation-gate.js +281 -26
  303. package/src/runtime/intake/engine.js +677 -0
  304. package/src/runtime/intake/index.js +136 -0
  305. package/src/runtime/intake/manifest.js +140 -0
  306. package/src/runtime/intake/taxonomy.js +206 -0
  307. package/src/runtime/inventory/coverage.js +1 -1
  308. package/src/runtime/inventory/index.js +1 -1
  309. package/src/runtime/inventory/precedence.js +2 -2
  310. package/src/runtime/inventory/siblings.js +6 -4
  311. package/src/runtime/knowledge/code-corpus.js +277 -0
  312. package/src/runtime/knowledge/code-lexical.js +130 -0
  313. package/src/runtime/knowledge/code-retrieval.js +307 -0
  314. package/src/runtime/knowledge/code-validation.js +106 -0
  315. package/src/runtime/knowledge/gp3-gate.js +74 -0
  316. package/src/runtime/knowledge/graph-adapter.js +288 -0
  317. package/src/runtime/knowledge/graph-fallback.js +394 -0
  318. package/src/runtime/knowledge/hybrid.js +136 -8
  319. package/src/runtime/knowledge/retrieval.js +34 -1
  320. package/src/runtime/knowledge/rtk-compress.js +77 -0
  321. package/src/runtime/knowledge/semble-bridge.py +106 -0
  322. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  323. package/src/runtime/knowledge/semble-provider.js +365 -0
  324. package/src/runtime/lifecycle/cli.js +88 -10
  325. package/src/runtime/lifecycle/event-store.js +8 -6
  326. package/src/runtime/lifecycle/followup.js +38 -7
  327. package/src/runtime/lifecycle/overview.js +26 -2
  328. package/src/runtime/lifecycle/report-store.js +8 -10
  329. package/src/runtime/lifecycle/retention.js +95 -31
  330. package/src/runtime/lifecycle/root.js +9 -1
  331. package/src/runtime/lifecycle/status.js +34 -17
  332. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  333. package/src/runtime/mask.js +134 -7
  334. package/src/runtime/mcp-consultations.js +40 -6
  335. package/src/runtime/model-router.js +79 -7
  336. package/src/runtime/models/catalogue.js +191 -0
  337. package/src/runtime/models/command.js +153 -0
  338. package/src/runtime/off-words.js +12 -0
  339. package/src/runtime/outcome.js +2 -2
  340. package/src/runtime/packages/command.js +1009 -0
  341. package/src/runtime/packages/contributions.js +116 -0
  342. package/src/runtime/packages/digest.js +132 -0
  343. package/src/runtime/packages/fetch.js +182 -0
  344. package/src/runtime/packages/grants.js +192 -0
  345. package/src/runtime/packages/index-file.js +134 -0
  346. package/src/runtime/packages/instruction.js +90 -0
  347. package/src/runtime/packages/manifest.js +331 -0
  348. package/src/runtime/packages/points.js +487 -0
  349. package/src/runtime/packages/programs.js +216 -0
  350. package/src/runtime/packages/scaffold.js +277 -0
  351. package/src/runtime/packages/source.js +90 -0
  352. package/src/runtime/packages/state.js +164 -0
  353. package/src/runtime/packages/store.js +449 -0
  354. package/src/runtime/packages/tar.js +106 -0
  355. package/src/runtime/packages/view.js +406 -0
  356. package/src/runtime/plan-check.js +59 -0
  357. package/src/runtime/plan-content.js +324 -0
  358. package/src/runtime/policy-init.js +118 -0
  359. package/src/runtime/profiles/doctor.js +96 -0
  360. package/src/runtime/profiles/resolve.js +244 -0
  361. package/src/runtime/profiles/rollback.js +254 -0
  362. package/src/runtime/profiles/sampler.js +41 -0
  363. package/src/runtime/readiness-record.js +16 -11
  364. package/src/runtime/readiness.js +46 -5
  365. package/src/runtime/research-request.js +50 -25
  366. package/src/runtime/retrieval-plan.js +10 -30
  367. package/src/runtime/retrieval-record.js +75 -5
  368. package/src/runtime/review/command.js +246 -0
  369. package/src/runtime/review/dominance.js +68 -0
  370. package/src/runtime/review/findings.js +434 -0
  371. package/src/runtime/review/identity.js +255 -0
  372. package/src/runtime/review/protocol-command.js +109 -0
  373. package/src/runtime/review/protocols.js +405 -0
  374. package/src/runtime/review/resolve-protocol.js +302 -0
  375. package/src/runtime/review/start.js +530 -0
  376. package/src/runtime/roadmap/attendance.js +137 -0
  377. package/src/runtime/roadmap/facts.js +479 -0
  378. package/src/runtime/roadmap/gates.js +273 -0
  379. package/src/runtime/roadmap/loop.js +1719 -0
  380. package/src/runtime/roadmap/merge.js +390 -0
  381. package/src/runtime/roadmap/rows.js +1122 -0
  382. package/src/runtime/roadmap/seed.js +1063 -0
  383. package/src/runtime/roadmap/worktree-cleanup.js +147 -0
  384. package/src/runtime/rules-report.js +336 -0
  385. package/src/runtime/scaffold/artifacts.js +60 -9
  386. package/src/runtime/scaffold/generate.js +21 -9
  387. package/src/runtime/settings/autopilot.js +106 -0
  388. package/src/runtime/settings/cli.js +280 -0
  389. package/src/runtime/settings/config-write.js +314 -0
  390. package/src/runtime/settings/files.js +271 -0
  391. package/src/runtime/settings/legacy.js +154 -0
  392. package/src/runtime/settings/lexical.js +56 -0
  393. package/src/runtime/settings/prepared-run.js +269 -0
  394. package/src/runtime/settings/project-layers.js +118 -0
  395. package/src/runtime/settings/refs.js +288 -0
  396. package/src/runtime/settings/registry.js +553 -0
  397. package/src/runtime/settings/resolve.js +713 -0
  398. package/src/runtime/settings/shared.js +37 -0
  399. package/src/runtime/settings/worker-billing.js +107 -0
  400. package/src/runtime/settings/write.js +269 -0
  401. package/src/runtime/slot-command.js +78 -0
  402. package/src/runtime/slots.js +671 -0
  403. package/src/runtime/source-version.js +1 -0
  404. package/src/runtime/state-root.js +217 -0
  405. package/src/runtime/storage/cli.js +12 -5
  406. package/src/runtime/storage/engine.js +9 -2
  407. package/src/runtime/storage/registry.js +14 -1
  408. package/src/runtime/storage/roots.js +66 -16
  409. package/src/runtime/storage/rules.js +82 -10
  410. package/src/runtime/storage/upkeep.js +10 -2
  411. package/src/runtime/storage/work-status.js +245 -26
  412. package/src/runtime/task-attempts.js +589 -69
  413. package/src/runtime/task-classifier.js +85 -14
  414. package/src/runtime/task-graph.js +373 -0
  415. package/src/runtime/task-run.js +778 -0
  416. package/src/runtime/task-scope.js +176 -55
  417. package/src/runtime/task-sources.js +147 -0
  418. package/src/runtime/task-state.js +373 -64
  419. package/src/runtime/team/admission.js +301 -0
  420. package/src/runtime/team/envelope.js +324 -0
  421. package/src/runtime/team/review-guard.js +156 -0
  422. package/src/runtime/trace/analyze.js +427 -0
  423. package/src/runtime/trace/baseline.js +770 -0
  424. package/src/runtime/trace/ledger.js +63 -140
  425. package/src/runtime/trace/otlp-export.js +568 -0
  426. package/src/runtime/trace/otlp-map.js +481 -0
  427. package/src/runtime/trace/spans.js +745 -0
  428. package/src/runtime/trace/telemetry.js +225 -0
  429. package/src/runtime/trace/views.js +1 -0
  430. package/src/runtime/transcripts/command.js +98 -0
  431. package/src/runtime/transcripts/cursor.js +326 -0
  432. package/src/runtime/transcripts/read.js +126 -0
  433. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  434. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  435. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  436. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  437. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  438. package/src/runtime/transcripts/registry.js +161 -0
  439. package/src/runtime/transcripts/stats.js +161 -0
  440. package/src/runtime/verb-view.js +267 -0
  441. package/src/runtime/verification/check-key.js +628 -0
  442. package/src/runtime/verification/contract-runner.js +546 -11
  443. package/src/runtime/verification/engine.js +499 -54
  444. package/src/runtime/verification/impact.js +617 -0
  445. package/src/runtime/verification/inflight.js +205 -0
  446. package/src/runtime/verification/registry.js +176 -0
  447. package/src/runtime/verification/reuse.js +184 -0
  448. package/src/runtime/verification/telemetry-emit.js +254 -0
  449. package/src/runtime/verification/test-selection.js +67 -7
  450. package/src/runtime/worker/active.js +90 -0
  451. package/src/runtime/worker/cancel.js +222 -0
  452. package/src/runtime/worker/catalogue.js +122 -0
  453. package/src/runtime/worker/command.js +677 -0
  454. package/src/runtime/worker/evidence.js +288 -0
  455. package/src/runtime/worker/launch.js +256 -0
  456. package/src/runtime/worker/models.js +137 -0
  457. package/src/runtime/worker/parsers.js +218 -0
  458. package/src/runtime/worker/probes/index.js +706 -0
  459. package/src/runtime/worker/process-tree.js +415 -0
  460. package/src/runtime/worker/quota.js +43 -0
  461. package/src/runtime/worker/readiness.js +713 -0
  462. package/src/runtime/worker/run-store.js +166 -0
  463. package/src/runtime/worker/supervisor.js +541 -0
  464. package/src/runtime/worker/worktree-check.js +252 -0
  465. package/src/runtime/workflow-engine.js +5 -1
  466. package/src/runtime/workflow-orchestrator.js +610 -67
  467. package/src/runtime/worktree.js +149 -22
  468. package/src/state/index.js +1 -1
  469. package/src/state/lockfile.js +64 -10
  470. package/src/state/run-lock.js +1 -1
  471. package/types/node-commonjs.d.ts +106 -7
  472. package/core/harnesses/claude/hooks/pre-bash-guard.sh +0 -10
  473. package/core/harnesses/codex/hooks/pre-bash-guard.sh +0 -10
  474. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  475. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  476. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
  477. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  478. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
  479. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  480. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
  481. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  482. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  483. package/core/harnesses/gemini/hooks/session-start.sh +0 -7
  484. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  485. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +0 -24
  486. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +0 -91
  487. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +0 -378
  488. package/core/shared/guidance/FLAGS.md +0 -25
  489. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  490. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  491. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  492. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  493. package/src/adapters/gemini/hooks.js +0 -251
  494. package/src/adapters/gemini/index.js +0 -429
@@ -52,6 +52,7 @@ function projectAdapterInput({ registry, harness, scope, scopeRoot, assets = [],
52
52
  // source paths, which is all of them except Antigravity's directory-per-agent requirement.
53
53
  layout: projection.layout ?? null,
54
54
  transform: projection.transform ?? null,
55
+ select: projection.select ?? null,
55
56
  prerequisites: [...(capability.prerequisites || [])],
56
57
  });
57
58
  };
@@ -7,11 +7,13 @@
7
7
  // Kiro's native root is symmetric across scopes — `.kiro/` (project) / `~/.kiro/` (global) is the
8
8
  // one directory that holds everything Kiro reads: steering, agents, hooks, and settings/mcp.json
9
9
  // (see src/targets.js's toolDirs() for the same convention this adapter's own nativePaths()
10
- // mirrors). Unlike every other harness in this codebase, Kiro receives DoFlow's *full* guidance
11
- // tree as steering files rather than a single marker-wrapped pointer file plus a separately-copied
12
- // tree (design.md §4's "research decision D3"): there is no single-file instruction surface here
13
- // at all, so — unlike src/adapters/gemini or src/adapters/opencode — this adapter never renders a
14
- // marker-wrapped instruction file.
10
+ // mirrors). Unlike every other harness in this codebase, Kiro receives DoFlow's guidance as
11
+ // steering files rather than a single marker-wrapped pointer file plus a separately-copied tree
12
+ // (design.md §4's "research decision D3"): there is no single-file instruction surface here at all,
13
+ // so — unlike src/adapters/opencode — this adapter never renders a
14
+ // marker-wrapped instruction file. Steering holds the core set only (DOFLOW_CORE.md and the files it
15
+ // imports, selected by the `guidance-manifest` select); the full guidance tree remains under
16
+ // `.doflow/guidance` through kiro.guidance-tree, where `retrieve` and the runtime read it.
15
17
  //
16
18
  // Evidence: https://kiro.dev/docs/steering/, https://kiro.dev/docs/mcp/configuration/,
17
19
  // https://kiro.dev/docs/hooks/
@@ -37,6 +39,15 @@ const LEGACY_MCP_REGISTRATION = `${HARNESS}:mcp:registration`;
37
39
  * module-level exports delegate to a default-configured instance so direct callers keep their
38
40
  * historical shape.
39
41
  */
42
+ /** Hook tiers (maint-hooks): with Kiro outside `hooks.harnesses` (`context.hooksOff`) its agents carry no
43
+ * `hooks`, so they never name a hook script DoFlow did not install. */
44
+ const CONTENT_ONLY_TRANSFORMS = Object.freeze({ 'kiro-agents': 'kiro-agents-content' });
45
+ function transformFor(asset , context ) {
46
+ if (context?.hooksOff !== true) return asset.transform || null;
47
+ if (asset.id === 'kiro.doflow-agent') return 'kiro-agent-content';
48
+ return CONTENT_ONLY_TRANSFORMS[asset.transform] ?? (asset.transform || null);
49
+ }
50
+
40
51
  function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } = {}) {
41
52
  // Parity note: Kiro's historical nativePaths() derived even its user-scope root from scopeRoot
42
53
  // (the CLI passes $HOME there for -g installs), never from a separate homeDir argument — so the
@@ -59,8 +70,8 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
59
70
 
60
71
  /**
61
72
  * Kiro has no single-file, marker-managed instruction surface for this adapter to render into —
62
- * the guidance tree is projected file-for-file by the copy-tree engine below, once as steering
63
- * (guidance.context-layer) and once into the shared `.doflow/guidance` (kiro.guidance-tree). Every
73
+ * guidance is projected file-for-file by the copy-tree engine below, the core set as steering
74
+ * (guidance.context-layer) and the whole tree into the shared `.doflow/guidance` (kiro.guidance-tree). Every
64
75
  * asset routed to 'kiro' in core/registry/assets.json uses `renderer: copy-tree` or
65
76
  * `kiro-agents`, so nothing reaches this function. It is kept as a pure passthrough only to
66
77
  * satisfy the six-function adapter contract every harness must implement identically.
@@ -98,7 +109,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
98
109
  const destDir = copyTreeDestDir(paths.configDir, asset);
99
110
  const sourceDir = sourceDirFor(asset, context, fsImpl, 'Kiro');
100
111
  const previousResources = ledgerFileResources(ledger?.resources, HARNESS, asset.id);
101
- const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, transform: asset.transform, keepModified: !removing,
112
+ const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, transform: transformFor(asset, context), select: asset.select, keepModified: !removing,
102
113
  // Forwarded so the CLI's --force reaches planTree's conflict check; omitting it let
103
114
  // planTree's own `force = false` default stand in silently. Gated on `!removing` for the
104
115
  // reason codex/index.ts states in full: force heals drift on apply, but a hand-edited file
@@ -115,7 +126,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
115
126
  ownershipIdentity: `doflow:${HARNESS}:copy-tree:${asset.id}:${change.relPath}`,
116
127
  kind: 'copy-tree-file', identity: change.relPath,
117
128
  afterFingerprint: change.fingerprint, fingerprint: change.fingerprint, sourceVersion: 'registry-v1',
118
- transformName: asset.transform || null,
129
+ transformName: transformFor(asset, context),
119
130
  ...(change.kept ? { retained: true, retainedFor: [] } : {}),
120
131
  projection: { renderer: asset.renderer },
121
132
  });
@@ -153,7 +164,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
153
164
  for (const asset of kiroTreeAssets(assets)) {
154
165
  const destDir = copyTreeDestDir(paths.configDir, asset);
155
166
  const sourceDir = sourceDirFor(asset, context, fsImpl, 'Kiro');
156
- const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.transform, plannedWrites: context?.plannedWrites });
167
+ const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: transformFor(asset, context), select: asset.select, plannedWrites: context?.plannedWrites });
157
168
  conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
158
169
  for (const resource of result.resources) {
159
170
  resources.push({
@@ -209,8 +220,8 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
209
220
 
210
221
  /** Kiro has no hooks or mcp asset of its own in core/registry/assets.json (mcp servers come from
211
222
  * core/registry/mcp.json, not an asset), so an MCP entry change piggybacks on an asset id
212
- * this harness actually receives — the same "pseudo-component" technique
213
- * src/adapters/gemini/index.js#hooksAssetId already uses for its own settings-only component. */
223
+ * this harness actually receives — the same "pseudo-component" technique the opencode and
224
+ * copilot adapters use for their settings-only changes. */
214
225
  function pseudoAssetId(assets ) {
215
226
  return assets.find((asset ) => asset.id === 'guidance.context-layer')?.id ?? assets[0]?.id;
216
227
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  // OpenCode adapter.
6
6
  //
7
- // OpenCode gets its own copy of DoFlow's skills tree, the same as Claude, Codex, and Gemini: it
7
+ // OpenCode gets its own copy of DoFlow's skills tree, the same as Claude and Codex: it
8
8
  // natively discovers skills under `.opencode/skills/` (project) and `~/.config/opencode/skills/`
9
9
  // (global) without any settings entry, so the shared copy-tree engine in ../copy-tree.ts
10
10
  // materialises `skills.doflow` there directly. What remains registration, rather than
@@ -20,11 +20,11 @@ const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDes
20
20
  const { readMcpFiles, planMcpEntries, verifyMcpEntries, ownedMcpIds, writeMcpEntries } = require('../mcp-entries.js');
21
21
 
22
22
  // Only the marker constants: marker-merge.js reads and writes files itself, which cannot be used
23
- // from plan(), whose contract is to compute changes without touching disk. The gemini adapter
24
- // solves this the same way, with a pure managedInstruction() over strings.
23
+ // from plan(), whose contract is to compute changes without touching disk.
25
24
  const { MARKER_START, MARKER_END } = require('../../helper/marker-merge.js');
26
25
  const { destructiveCommandGlobs } = require('../../helper/guardrails.js');
27
26
  const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
27
+ const { renderGuidancePointer } = require('../guidance-pointer.js');
28
28
 
29
29
  const HARNESS = 'opencode';
30
30
  /**
@@ -107,9 +107,8 @@ function createOpenCodeAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS]
107
107
  }
108
108
 
109
109
  /** Replace only the span between DoFlow's markers, leaving everything else byte-identical.
110
- * An AGENTS.md that exists without a DoFlow section is refused rather than appended to — the same
111
- * policy the gemini adapter applies to GEMINI.md, and for the same reason: AGENTS.md is a shared,
112
- * cross-tool file, so where DoFlow's content belongs inside it is a decision for its owner, not a
110
+ * An AGENTS.md that exists without a DoFlow section is refused rather than appended to:
111
+ * AGENTS.md is a shared, cross-tool file, so where DoFlow's content belongs inside it is a decision for its owner, not a
113
112
  * guess for the installer. Add an empty marker pair to opt in. */
114
113
  /** Opt-in (--permissions) guardrail projection: DoFlow's destructive-command blocklist lands as
115
114
  * deny rules under permission.bash. Only keys this feature owns (each carries a matching
@@ -259,7 +258,7 @@ function createOpenCodeAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS]
259
258
  /** OpenCode's settings-merge change has no asset of its own in core/registry/assets.json (it's a
260
259
  * registration side-effect of the instructions/mcp assets, not a projected asset), so it piggybacks
261
260
  * on a real asset id already routed to this harness — the same "pseudo-component" technique
262
- * src/adapters/gemini/index.js#hooksAssetId and src/adapters/kiro/index.js#pseudoAssetId use. The
261
+ * src/adapters/kiro/index.js#pseudoAssetId uses. The
263
262
  * lifecycle layer validates every change's assetId against the harness's actual asset list, so an
264
263
  * invented id like the literal string 'opencode.config' is rejected as unknown. */
265
264
  function pseudoAssetId(assets ) {
@@ -308,7 +307,8 @@ function createOpenCodeAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS]
308
307
 
309
308
  const guidance = assets.find((asset ) => asset.capability === 'instructions');
310
309
  if (guidance) {
311
- const rendered = render({ content: fsImpl.readFileSync(path.resolve(context.repoRoot, guidance.source), 'utf8') });
310
+ const pointer = renderGuidancePointer(fsImpl.readFileSync(path.resolve(context.repoRoot, guidance.source), 'utf8'), { scope, scopeRoot });
311
+ const rendered = render({ content: pointer });
312
312
  const outcome = removing
313
313
  ? { ok: true, operation: 'remove', content: strippedInstruction(found.instruction) }
314
314
  : managedInstruction(found.instruction, rendered);
@@ -93,7 +93,8 @@ function verifyPiHooks({ scope, scopeRoot, assets = [], ledger, context = {}, ad
93
93
  const plannedWrite = (context?.plannedWrites || []).includes(target);
94
94
  const bridgeMatches = !!content && content.includes(JSON.stringify(path.relative(path.dirname(target), bridgePath).split(path.sep).join('/')));
95
95
  const commandOnPath = (command ) => String(pathEnv || '').split(path.delimiter)
96
- .some((dir) => fsImpl.existsSync(path.join(dir, command)));
96
+ .some((dir) => [command, ...(process.platform === 'win32' ? ['.exe', '.cmd', '.bat'].map((ext) => command + ext) : [])]
97
+ .some((name) => fsImpl.existsSync(path.join(dir, name))));
97
98
  const status = present && !owned && !plannedWrite && !adopt ? 'conflict'
98
99
  : (present && (fingerprintMatches || plannedWrite) && bridgeMatches && commandOnPath('bash') && commandOnPath('jq') ? 'managed' : 'absent');
99
100
  const resources = status === 'managed' ? [{ assetId: ASSET_ID, target, ownershipIdentity: OWNERSHIP,
@@ -3,7 +3,7 @@
3
3
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
4
 
5
5
  // Pi adapter — a marker-merged instruction file plus copy-tree materialisation, the same shape
6
- // Claude/Codex/Gemini already use for their own skills trees.
6
+ // Claude/Codex already use for their own skills trees.
7
7
  //
8
8
  // Skills materialise into Pi's own directories (`~/.pi/agent/skills` global, `.pi/skills` project)
9
9
  // via the shared copy-tree engine, rather than pointing Pi's settings.json at DoFlow's already
@@ -22,10 +22,11 @@ const fs = require('node:fs');
22
22
  const path = require('node:path');
23
23
 
24
24
  const { MARKER_START, MARKER_END } = require('../../helper/marker-merge.js');
25
- const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, sharedTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, sourceDirFor } = require('../copy-tree.js');
25
+ const { planTree, applyTree, removeTree, verifyTree, copyTreeAssets, copyTreeDestDir, sharedTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, fingerprint, sourceDirFor, resolveTransform } = require('../copy-tree.js');
26
26
  const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
27
27
  const { mcpRows, discoverPiMcp, ownedPiMcpIds, planPiMcp, applyPiMcp, verifyPiMcp } = require('./mcp.js');
28
28
  const { planPiHooks, verifyPiHooks, removePiHooks } = require('./hooks.js');
29
+ const { renderGuidancePointer } = require('../guidance-pointer.js');
29
30
 
30
31
  const HARNESS = 'pi';
31
32
  const AGENT_DIR_MCP_NOTICE = 'PI_CODING_AGENT_DIR is set: Pi reads its whole agent dir from it, but DoFlow moves only mcp.json there; skills and AGENTS.md stay in ~/.pi/agent.';
@@ -105,7 +106,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
105
106
  const destDir = sharedTreeDestDir(paths.root, asset.nativeDir) ?? copyTreeDestDir(paths.configDir, asset);
106
107
  const sourceDir = sourceDirFor(asset, context, fsImpl, 'Pi');
107
108
  const previousResources = ledgerFileResources(ledger?.resources, HARNESS, asset.id);
108
- const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, keepModified: !removing,
109
+ const result = planTree({ sourceDir, destDir, previousResources, siblingFingerprints: ledgerSiblingFingerprints(ledger?.resources, HARNESS), operation: removing ? 'remove' : 'apply', fsImpl, layout: asset.layout, transform: asset.transform, keepModified: !removing,
109
110
  // Forwarded so the CLI's --force reaches planTree's conflict check; omitting it let
110
111
  // planTree's own `force = false` default stand in silently. Gated on `!removing` for the
111
112
  // reason codex/index.ts states in full: force heals drift on apply, but a hand-edited file
@@ -123,6 +124,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
123
124
  kind: 'copy-tree-file', identity: change.relPath,
124
125
  afterFingerprint: change.fingerprint, fingerprint: change.fingerprint, sourceVersion: 'registry-v1',
125
126
  ...(change.kept ? { retained: true, retainedFor: [] } : {}),
127
+ transformName: asset.transform || null,
126
128
  projection: { renderer: 'copy-tree' },
127
129
  });
128
130
  }
@@ -132,8 +134,12 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
132
134
 
133
135
  function applyCopyTreeAssets(changes , { fsImpl = fs } = {}) {
134
136
  const treeChanges = changes.filter((change ) => change.projection?.renderer === 'copy-tree' && change.operation !== 'remove')
135
- .map((change ) => ({ relPath: change.identity, target: change.target, source: change.source, operation: change.operation, fingerprint: change.fingerprint }));
136
- return applyTree({ changes: treeChanges, fsImpl }).applied;
137
+ .map((change ) => ({ relPath: change.identity, target: change.target, source: change.source, operation: change.operation, fingerprint: change.fingerprint, transformName: change.transformName || null }));
138
+ const grouped = new Map ();
139
+ for (const change of treeChanges) grouped.set(change.transformName, [...(grouped.get(change.transformName) || []), change]);
140
+ let applied = 0;
141
+ for (const [transformName, group] of grouped) applied += applyTree({ changes: group, fsImpl, transform: resolveTransform(transformName) }).applied;
142
+ return applied;
137
143
  }
138
144
 
139
145
  function removeCopyTreeAssets(changes , { fsImpl = fs } = {}) {
@@ -150,7 +156,7 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
150
156
  for (const asset of copyTreeAssets(assets)) {
151
157
  const destDir = sharedTreeDestDir(paths.root, asset.nativeDir) ?? copyTreeDestDir(paths.configDir, asset);
152
158
  const sourceDir = sourceDirFor(asset, context, fsImpl, 'Pi');
153
- const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, plannedWrites: context?.plannedWrites });
159
+ const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.transform, plannedWrites: context?.plannedWrites });
154
160
  conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
155
161
  for (const resource of result.resources) {
156
162
  resources.push({
@@ -175,7 +181,8 @@ function createPiAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS], env
175
181
 
176
182
  const guidance = assets.find((asset ) => asset.capability === 'instructions');
177
183
  if (guidance) {
178
- const rendered = render({ content: fsImpl.readFileSync(path.resolve(context.repoRoot, guidance.source), 'utf8') });
184
+ const pointer = renderGuidancePointer(fsImpl.readFileSync(path.resolve(context.repoRoot, guidance.source), 'utf8'), { scope, scopeRoot });
185
+ const rendered = render({ content: pointer });
179
186
  const outcome = removing
180
187
  ? { ok: true, operation: 'remove', content: strippedInstruction(found.instruction) }
181
188
  : managedInstruction(found.instruction, rendered);
@@ -2,12 +2,36 @@
2
2
 
3
3
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
4
 
5
+ const fs = require('node:fs');
5
6
  const path = require('node:path');
6
7
 
7
8
  /** Resolve a registry-declared source path against the repo root, refusing escapes. Shared by the
8
9
  * common projection module and by per-adapter native-projection normalizers, which is why it lives
9
- * in its own module rather than in index.js — a normalizer importing index.js would be a cycle. */
10
- function resolveRegistrySource(registry , source , label ) {
10
+ * in its own module rather than in index.js — a normalizer importing index.js would be a cycle.
11
+ * `sourceRoot` checks containment against that root using real paths, as required by IC-017.
12
+ * When omitted, the existing lexical repository-root check is preserved. */
13
+ function resolveRegistrySource(registry , source , label , sourceRoot ) {
14
+ if (sourceRoot !== undefined) {
15
+ if (typeof sourceRoot !== 'string' || !sourceRoot) {
16
+ throw new Error(`Registry ${label} sourceRoot must be a non-empty string`);
17
+ }
18
+ if (typeof source !== 'string' || !source) throw new Error(`Registry ${label} source is required`);
19
+ let realRoot ;
20
+ let realSource ;
21
+ try {
22
+ realRoot = fs.realpathSync(path.resolve(sourceRoot));
23
+ realSource = fs.realpathSync(path.resolve(sourceRoot, source));
24
+ } catch (error ) {
25
+ if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') {
26
+ throw new Error(`Registry ${label} source is missing under source root: ${source}`);
27
+ }
28
+ throw error;
29
+ }
30
+ if (!realSource.startsWith(`${realRoot}${path.sep}`)) {
31
+ throw new Error(`Registry ${label} source escapes source root: ${source}`);
32
+ }
33
+ return realSource;
34
+ }
11
35
  if (typeof registry.repoRoot !== 'string' || !registry.repoRoot) throw new Error(`Registry repoRoot is required for ${label}`);
12
36
  if (typeof source !== 'string' || !source) throw new Error(`Registry ${label} source is required`);
13
37
  const root = path.resolve(registry.repoRoot);
@@ -20,7 +20,7 @@ const { loadRegistry } = require('../../registry/index.js');
20
20
  const { storageAfterInstall } = require('../../runtime/storage/upkeep.js');
21
21
  const { applyLifecycle, applyMcpIndex, recordMcpOwnership } = require('../../lifecycle/index.js');
22
22
  const {
23
- codexScope, registryLifecycleView, printRegistryLifecycle, printPlanNotices, assertSafeRegistryPlan,
23
+ codexScope, registryLifecycleView, printRegistryLifecycle, printRetiredPlans, printPlanNotices, assertSafeRegistryPlan,
24
24
  lockDocument, recordLock,
25
25
  } = require('../../lifecycle/view.js');
26
26
  const {
@@ -120,6 +120,7 @@ function cmdInstall(o ) {
120
120
  const owned = result.ledger.resources.filter((resource ) => resource.harness === target.harness).length;
121
121
  console.log(`[INFO] ${target.harness}: lifecycle verified (${owned} owned resource(s))`);
122
122
  }
123
+ printRetiredPlans(lifecycleView);
123
124
  ledgerAfter = result.ledger;
124
125
  } else {
125
126
  // MCP_INDEX.md is generated, not a tracked resource, so it never appears in plan.changes and
@@ -2,13 +2,19 @@
2
2
  // `doflow reconcile` — converge observed state onto the doflow.lock pin: report drift, then heal
3
3
  // it on confirmation. Never re-prompts for MCP; selections ride exactly as pinned.
4
4
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
5
+ const path = require('node:path');
5
6
  const { toolDirs } = require('../../install/targets.js');
6
7
  const { resolveContext, printContext } = require('../../install/context.js');
7
8
  const { writeManifest } = require('../../install/manifest.js');
9
+ const { DEFAULT_BACKUP_RETENTION, createFileBackup } = require('../../install/backup.js');
10
+ const { backupSetFromPlan, backupSetFromPaths } = require('../../install/backup-set.js');
11
+ const { ledgerPath } = require('../../state/index.js');
12
+ const { backupStep, reportBackup, applyBackupRetention } = require('./install.js');
8
13
  const { confirm } = require('../../helper/prompt.js');
9
14
  const { sourceCommit } = require('../../helper/git.js');
10
- const { loadRegistry } = require('../../registry/index.js');
15
+ const { loadRegistry, retiredFor } = require('../../registry/index.js');
11
16
  const { applyLifecycle, recordMcpOwnership } = require('../../lifecycle/index.js');
17
+ const { formatRetiredPlans, retiredReleaseKeys } = require('../../lifecycle/retired-harnesses.js');
12
18
  const {
13
19
  registryLifecycleView, assertSafeRegistryPlan,
14
20
  } = require('../../lifecycle/view.js');
@@ -33,6 +39,10 @@ function reconcileReport(view ) {
33
39
  }
34
40
  perHarness.set(target.harness, entry);
35
41
  }
42
+ // A retired harness's rows (row 127 IC-003) are drift too: converging means releasing them.
43
+ for (const change of (view.plan.retiredHarnesses ?? []).flatMap((item ) => item.changes)) {
44
+ drifts.push({ harness: change.harness, operation: change.operation, assetId: change.assetId, target: change.target, retired: true });
45
+ }
36
46
  return {
37
47
  drifts,
38
48
  conflicts: view.plan.conflicts,
@@ -42,17 +52,29 @@ function reconcileReport(view ) {
42
52
  };
43
53
  }
44
54
 
45
- function printReconcileReport(report , lock ) {
55
+ function printReconcileReport(report , lock , retiredLines = []) {
46
56
  console.log(`[INFO] Reconciling against doflow.lock (sourceVersion ${lock.sourceVersion ?? 'unknown'})`);
47
57
  for (const [harness, counts] of Object.entries (report.perHarness)) {
48
58
  const total = counts.create + counts.update + counts.remove;
49
59
  console.log(`[INFO] ${harness}: ${total === 0 ? 'clean' : `${total} drift(s) (${counts.create} create, ${counts.update} update, ${counts.remove} remove)`}`);
50
60
  }
61
+ for (const line of retiredLines) console.log(line);
51
62
  for (const conflict of report.conflicts) console.log(`[CONFLICT] ${conflict.harness}: ${conflict.reason}`);
52
63
  for (const prerequisite of report.prerequisites) console.log(`[PENDING-TRUST] ${prerequisite.harness}: ${prerequisite.prerequisite}`);
53
64
  if (report.clean) console.log('[OK] Observed state matches doflow.lock.');
54
65
  }
55
66
 
67
+ /** The backup a retirement sweep takes (row 127 FR-4): the set install and update take, files and
68
+ * ledger. A sweep that only gives up rows (release, drop, keep) touches no file, so that set is empty;
69
+ * the ledger alone is then what a rollback needs to bring the rows and the target entry back. */
70
+ function retirementBackupSet(view , paths , scope ) {
71
+ const ledger = ledgerPath(view.stateRoot);
72
+ const args = { scope: scope.global ? 'global' : 'project', scopeRoot: paths.scopeRoot, records: [ledger],
73
+ exclude: [paths.backupRoot, view.stateRoot, paths.manifestPath, path.join(paths.doflowRoot, 'doflow.lock')] };
74
+ const set = backupSetFromPlan({ plan: view.plan, ...args });
75
+ return set.count ? set : backupSetFromPaths({ items: [{ path: ledger, harnesses: [] }], ...args });
76
+ }
77
+
56
78
  function cmdReconcile(o ) {
57
79
  refuseHomeRootedProject(o, 'reconcile');
58
80
  const scope = scopeOf(o);
@@ -61,23 +83,33 @@ function cmdReconcile(o ) {
61
83
  const dirs = toolDirs(scope);
62
84
  const lifecyclePaths = installPaths(scope);
63
85
  const { lock, ledger, manifestServers } = scopeSelectionState(scope);
86
+ // Read only when there is a pin or a row to classify, as before this check existed.
87
+ const registry = lock?.targets?.length || ledger?.resources?.length ? loadRegistry({ repoRoot: REPO_ROOT }) : null;
88
+ const retired = (harness ) => (registry ? retiredFor(registry, harness) : null);
89
+ // A retired pin (row 127 IC-004) names a harness with no adapter: dropped, said, and the rest
90
+ // converge. Its ledger rows are released by this run's plan, like install's and update's.
91
+ for (const entry of lock?.targets ?? []) {
92
+ const record = retired(entry.harness);
93
+ if (record) console.log(`[WARN] ${entry.harness}: retired (use ${record.replacement}); dropped from the pinned targets`);
94
+ }
95
+ const livePins = (lock?.targets ?? []).filter((entry ) => !retired(entry.harness));
64
96
  // A harness the ledger holds but the lock does not pin is installed and outside reconcile's reach.
65
97
  // Said, not converged: the lock records choices, and reconcile has no choice to record for it.
66
- const pinned = new Set((lock?.targets ?? []).map((entry ) => entry.harness));
67
- const unpinned = [...new Set((ledger?.resources ?? []).map((resource ) => resource.harness))].filter((harness) => !pinned.has(harness)).sort();
98
+ const pinned = new Set(livePins.map((entry ) => entry.harness));
99
+ const unpinned = [...new Set((ledger?.resources ?? []).map((resource ) => resource.harness))]
100
+ .filter((harness ) => !pinned.has(harness) && !retired(harness)).sort();
68
101
  for (const harness of unpinned) {
69
102
  const count = ledger.resources.filter((resource ) => resource.harness === harness).length;
70
103
  console.log(`[WARN] ${harness}: installed (ledger holds ${count} resource(s)) but absent from doflow.lock; reconcile does not converge it.`);
71
104
  }
72
- if (!lock || !lock.targets.length) {
105
+ if (!lock || !livePins.length) {
73
106
  // Reconcile converges onto what install pinned; with no pin there is no desired state to
74
107
  // converge to, and guessing one from the registry would silently adopt targets the user
75
108
  // never chose.
76
109
  console.log('[INFO] No doflow.lock in this scope — nothing pinned to reconcile against. Run `doflow install` first.');
77
110
  return;
78
111
  }
79
- const targets = lock.targets.map((entry ) => entry.harness);
80
- const registry = loadRegistry({ repoRoot: REPO_ROOT });
112
+ const targets = livePins.map((entry ) => entry.harness);
81
113
  printContext(resolveContext({ repoRoot: REPO_ROOT, targets, dirs, sourceCommit: sourceCommit(SCRIPT_DIR), ...scope }));
82
114
  // Each harness converges onto its own pinned selection — reconcile never re-prompts and never
83
115
  // lends one harness's servers to another.
@@ -87,7 +119,7 @@ function cmdReconcile(o ) {
87
119
  if (!lifecycleView.plan.safe) { assertSafeRegistryPlan(lifecycleView); return; }
88
120
 
89
121
  const report = reconcileReport(lifecycleView);
90
- printReconcileReport(report, lock);
122
+ printReconcileReport(report, lock, formatRetiredPlans(lifecycleView.plan.retiredHarnesses ?? []));
91
123
  if (o.json) console.log(JSON.stringify({ scope: lock.scope, sourceVersion: lock.sourceVersion, targets, unpinned, ...report }, null, 2));
92
124
  if (o.dryRun) {
93
125
  console.log('[DRY] Reconcile plan complete — no changes written');
@@ -107,10 +139,22 @@ function cmdReconcile(o ) {
107
139
  process.exit(1);
108
140
  }
109
141
  checkpointRunLock(hold);
142
+ // A run that changes a retired harness's rows backs the retirement up first, as install and
143
+ // update do, so `doflow rollback` restores it (row 127 FR-4). Any other reconcile takes none.
144
+ let bid = '';
145
+ const retiring = retiredReleaseKeys(lifecycleView.plan.retiredHarnesses ?? []).size > 0;
146
+ if (retiring && o.noBackup) {
147
+ console.error('[WARN] Skipping backup (--no-backup)');
148
+ } else if (retiring) {
149
+ const commit = sourceCommit(SCRIPT_DIR);
150
+ bid = reportBackup(backupStep(() => createFileBackup({ operation: 'reconcile', set: retirementBackupSet(lifecycleView, lifecyclePaths, scope),
151
+ backupRoot: lifecyclePaths.backupRoot, repoRoot: SCRIPT_DIR, sourceCommit: commit, version: pkg.version, date: new Date() })));
152
+ }
110
153
  applyLifecycle({ plan: lifecycleView.plan, registry: lifecycleView.registry,
111
154
  adapters: buildAdapterRegistry(),
112
155
  stateRoot: lifecycleView.stateRoot, ledger: lifecycleView.ledger });
113
- writeManifest({ scopeRoot: lifecyclePaths.scopeRoot, scriptVersion: pkg.version, operation: 'update', repoRoot: SCRIPT_DIR, sourceCommit: sourceCommit(SCRIPT_DIR), backupId: '', tools: targets, date: new Date() });
156
+ writeManifest({ scopeRoot: lifecyclePaths.scopeRoot, scriptVersion: pkg.version, operation: 'update', repoRoot: SCRIPT_DIR, sourceCommit: sourceCommit(SCRIPT_DIR), backupId: bid, tools: targets, date: new Date() });
157
+ if (bid) applyBackupRetention({ backupRoot: lifecyclePaths.backupRoot, prune: o.prune, keep: o.prune ?? DEFAULT_BACKUP_RETENTION, bid });
114
158
  console.log('[OK] Reconciliation complete — state converged onto doflow.lock.');
115
159
  } finally {
116
160
  hold?.release();
@@ -59,8 +59,8 @@ function cmdRemove(o ) {
59
59
  scope: codexScope(scope), scopeRoot: scope.global ? os.homedir() : path.resolve(scope.projectRoot),
60
60
  targets: lifecycleTargets, mcpAdoptable, retainedMcpIds, stateRoot: view.stateRoot, ledger: view.ledger,
61
61
  context: view.plan.targets[0].adapterInput.context });
62
- // Shared destinations (one .doflow/scripts tree for claude/codex/gemini, one .agents for
63
- // gemini/copilot) mean a removal can legitimately leave files standing. Saying only "removed"
62
+ // Shared destinations (one .doflow/scripts tree for claude/codex, one .agents for
63
+ // antigravity/copilot) mean a removal can legitimately leave files standing. Saying only "removed"
64
64
  // would be half the truth, so what was kept and who still claims it is printed, not implied.
65
65
  for (const line of retentionSummary(result.retained)) console.log(`[INFO] ${line}`);
66
66
  console.log(`[OK] Removed ${result.ledger.resources.length === 0 ? 'all' : 'eligible'} native resource(s) for ${lifecycleTargets.join(', ')}; ${result.ledger.resources.length} owned record(s) remain.`);
@@ -6,7 +6,7 @@
6
6
  // restores per tool, as it always did.
7
7
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
8
8
  const path = require('node:path');
9
- const { resolveTargets, toolDirs } = require('../../install/targets.js');
9
+ const { VALID, resolveTargets, toolDirs } = require('../../install/targets.js');
10
10
  const { resolveContext, printContext } = require('../../install/context.js');
11
11
  const {
12
12
  BackupError, createFileBackup, planRestore, executeRestore, listBackups, formatBytes,
@@ -59,9 +59,12 @@ function cmdRollback(o ) {
59
59
  fail(error);
60
60
  }
61
61
  // A per-file backup restores the harnesses it holds (only those in --target when given), so the
62
- // banner and the install manifest name those, not the default target.
62
+ // banner and the install manifest name those, not the default target. A backup taken by a run
63
+ // that swept a retired harness (127 IC-003) holds that harness's files and rows too: they are
64
+ // restored like any other, but a retired id is no target, so the banner and manifest skip it.
63
65
  const restoredTools = plan.format === 2
64
- ? [...new Set(plan.restore.flatMap((item ) => item.harnesses))].filter((h) => !explicitTargets || targets.includes(h)).sort()
66
+ ? [...new Set(plan.restore.flatMap((item ) => item.harnesses))]
67
+ .filter((h ) => VALID.includes(h) && (!explicitTargets || targets.includes(h))).sort()
65
68
  : targets;
66
69
  printContext(resolveContext({ repoRoot: REPO_ROOT, targets: restoredTools, dirs, sourceCommit: commit, global: o.global, projectRoot: '.' }));
67
70
 
@@ -2,32 +2,41 @@
2
2
  // `doflow self-update` — git fetch/pull the checkout, then re-run the install path so the
3
3
  // installed trees match the freshly pulled source.
4
4
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
5
+ const fs = require('node:fs');
6
+ const path = require('node:path');
5
7
  const { execFileSync } = require('node:child_process');
6
8
  const { REPO_ROOT } = require('../shared.js');
7
9
  const cmdInstall = require('./install.js');
8
10
 
9
- function cmdSelfUpdate(o ) {
11
+ /** The second argument is for tests: the checkout to update and the install it re-runs. */
12
+ function cmdSelfUpdate(o , { repoRoot = REPO_ROOT, install = cmdInstall } = {}) {
10
13
  console.log('[INFO] Self-update: checking for upstream changes...');
11
14
  let pulled = false;
12
15
  let fetchOk = false;
13
- try {
14
- execFileSync('git', ['-C', REPO_ROOT, 'fetch', '--depth=1', 'origin'], { stdio: ['ignore', 'ignore', 'pipe'], timeout: 10000 });
15
- fetchOk = true;
16
- } catch {
17
- console.error('[WARN] git fetch failed (offline or no remote configured) — installing from current HEAD');
16
+ // An npm install is not a git checkout: `git -C` there would act on whatever repository holds
17
+ // node_modules (or fail), never on DoFlow's source.
18
+ const isCheckout = fs.existsSync(path.join(repoRoot, '.git'));
19
+ if (!isCheckout) console.log('[INFO] This install is not a git checkout; to upgrade DoFlow run: npm install -g @khoavu882/doflow');
20
+ if (isCheckout) {
21
+ try {
22
+ execFileSync('git', ['-C', repoRoot, 'fetch', '--depth=1', 'origin'], { stdio: ['ignore', 'ignore', 'pipe'], timeout: 10000 });
23
+ fetchOk = true;
24
+ } catch {
25
+ console.error('[WARN] git fetch failed (offline or no remote configured) — installing from current HEAD');
26
+ }
18
27
  }
19
28
  if (fetchOk) {
20
29
  try {
21
- execFileSync('git', ['-C', REPO_ROOT, 'pull', '--ff-only'], { stdio: ['ignore', 'ignore', 'pipe'], timeout: 10000 });
30
+ execFileSync('git', ['-C', repoRoot, 'pull', '--ff-only'], { stdio: ['ignore', 'ignore', 'pipe'], timeout: 10000 });
22
31
  pulled = true;
23
- const commit = execFileSync('git', ['-C', REPO_ROOT, 'rev-parse', '--short', 'HEAD']).toString().trim();
32
+ const commit = execFileSync('git', ['-C', repoRoot, 'rev-parse', '--short', 'HEAD']).toString().trim();
24
33
  console.log(`[INFO] Updated to commit: ${commit}`);
25
34
  } catch {
26
35
  console.error('[WARN] Fast-forward not possible — using current local state');
27
36
  }
28
37
  }
29
38
  console.log(`[INFO] Running install (after ${pulled ? 'a git pull' : 'checking for updates'})...`);
30
- cmdInstall(o);
39
+ install(o);
31
40
  }
32
41
 
33
42
  module.exports = cmdSelfUpdate;
@@ -42,6 +42,10 @@ function cmdStatus(o ) {
42
42
  ledgerPresent: Boolean(readLedger(registryView.stateRoot)),
43
43
  plan: { changes: registryView.plan.changes.length, conflicts: registryView.plan.conflicts, prerequisites: registryView.plan.prerequisites },
44
44
  };
45
+ // Row 127: a retired harness the ledger still holds rows of, which the next install or update
46
+ // releases. Absent when there is none, so the report is unchanged for every other ledger.
47
+ const retired = (registryView.plan.retiredHarnesses ?? []).map((item ) => ({ harness: item.harness, replacement: item.replacement, rows: item.changes.length }));
48
+ if (retired.length) ctx.registry.retired = retired;
45
49
  // Per-harness status must derive from that harness's own plan target, not the flattened
46
50
  // registryView.plan.changes/conflicts across every target — otherwise, e.g., Codex having
47
51
  // pending changes when it isn't installed yet would make Claude's line falsely report
@@ -50,7 +54,7 @@ function cmdStatus(o ) {
50
54
  // Hook-wiring status needs the same general per-harness verification every install/update run
51
55
  // already flows through (src/lifecycle's verifyLifecycle) rather than a Codex-only hardcode:
52
56
  // it is the one place that can tell 'installed and active' apart from 'installed but pending a
53
- // prerequisite' (Codex's unreviewed trust, Gemini's live hook-trust check) from 'absent'.
57
+ // prerequisite' (Codex's unreviewed trust) from 'absent'.
54
58
  const verification = verifyLifecycle({ plan: registryView.plan, adapters: registryView.adapters, context: { registry } });
55
59
  const hooksFor = (harness ) => verification.verifications.find((item ) => item.harness === harness)?.hookWiring ?? null;
56
60
  const harnessStatus = (harness ) => {
@@ -80,8 +84,19 @@ function cmdStatus(o ) {
80
84
  ctx.registry = { status: 'invalid', error: error.message };
81
85
  }
82
86
 
87
+ // The primary is read on use only (row 120 A.4): a fault in resolving it never breaks status.
88
+ let primary = null;
89
+ let primaryText ;
90
+ try {
91
+ const { primarySummary, resolveForReport } = require('../../runtime/context/primary-command.js');
92
+ primary = resolveForReport({ env: process.env, projectRoot: path.resolve(scope.projectRoot) }).primary;
93
+ primaryText = primarySummary(primary);
94
+ } catch (error ) {
95
+ primaryText = `unavailable (${error?.message || String(error)})`;
96
+ }
97
+
83
98
  if (o.json) {
84
- console.log(JSON.stringify({ context: ctx, manifest, codex: ctx.codex ?? null, mcpSelections }, null, 2));
99
+ console.log(JSON.stringify({ context: ctx, manifest, codex: ctx.codex ?? null, mcpSelections, primary }, null, 2));
85
100
  return;
86
101
  }
87
102
 
@@ -92,6 +107,7 @@ function cmdStatus(o ) {
92
107
  };
93
108
 
94
109
  printContext(ctx);
110
+ console.log(`primary: ${primaryText}`);
95
111
  if (!manifest) {
96
112
  console.log("[WARN] No install manifest found — run 'doflow install' to get started");
97
113
  return;
@@ -120,12 +136,15 @@ function cmdStatus(o ) {
120
136
  if (line) console.log(line);
121
137
  if (status.errors.length) console.log(` ${`${label} issues:`.padEnd(24)}${status.errors.join('; ')}`);
122
138
  if (harness === 'codex') {
123
- console.log(' Codex capability gaps: no Claude-only event emulation is installed; review docs/capability-map.md#codex-capability-detail');
139
+ console.log(' Codex capability gaps: no Claude-only event emulation is installed');
124
140
  }
125
141
  }
126
142
  if (ctx.registry) {
127
143
  console.log(` Registry lifecycle: ${ctx.registry.status === 'invalid' ? ctx.registry.error : `${ctx.registry.plan.changes} pending, ${ctx.registry.plan.conflicts.length} conflict(s)`}`);
128
144
  if (ctx.registry.status !== 'invalid') console.log(` Neutral state: ${ctx.registry.stateRoot}${ctx.registry.ledgerPresent ? ' (ledger present)' : ' (not yet created)'}`);
145
+ for (const item of ctx.registry.retired ?? []) {
146
+ console.log(` Retired harness: ${item.harness} (use ${item.replacement}): ${item.rows} ledger row(s); the next 'doflow install' or 'doflow update' in this scope releases them`);
147
+ }
129
148
  }
130
149
  console.log('\n TOOL STATUS LAST UPDATED');
131
150
  for (const tool of LIFECYCLE_HARNESSES) {