@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
@@ -93,11 +93,18 @@ function classify(bodyLines , kinds , canonicalPointer
93
93
  return { comments, moved, pointer };
94
94
  }
95
95
 
96
- function writeAtomic(fsImpl , file , content ) {
96
+ function writeAtomic(fsImpl , file , content , unchangedSince ) {
97
97
  fsImpl.mkdirSync(path.dirname(file), { recursive: true });
98
98
  const tmp = `${file}.${process.pid}.tmp`;
99
99
  try {
100
100
  fsImpl.writeFileSync(tmp, content, 'utf8');
101
+ // The text `content` was built from is compared with the file just before the rename, so an edit
102
+ // made since it was read is kept instead of replaced (FU-1jbmm3). This narrows the window to the
103
+ // gap between this read and the rename; it cannot close it, which would take a lock on the
104
+ // artifact itself that its editors would also have to take.
105
+ if (unchangedSince !== undefined && fsImpl.readFileSync(file, 'utf8') !== unchangedSince) {
106
+ throw new Error(`${path.basename(file)} changed while it was being compacted`);
107
+ }
101
108
  fsImpl.renameSync(tmp, file);
102
109
  } catch (error ) {
103
110
  try { fsImpl.rmSync(tmp, { force: true }); } catch { /* nothing to clean */ }
@@ -189,7 +196,7 @@ function compactArtifact({ fsImpl, featureDir, file, date }
189
196
  const body = [blank];
190
197
  for (const c of comments) body.push(...c, blank);
191
198
  body.push(pointerLine, end === lines.length ? '' : blank);
192
- writeAtomic(fsImpl, file, [...lines.slice(0, start + 1), ...body, ...lines.slice(end)].join('\n'));
199
+ writeAtomic(fsImpl, file, [...lines.slice(0, start + 1), ...body, ...lines.slice(end)].join('\n'), text);
193
200
  return { artifact: name, path: rel, archive: archiveRel, lines: moved.length };
194
201
  }
195
202
 
@@ -204,7 +211,10 @@ function compactArtifact({ fsImpl, featureDir, file, date }
204
211
  * @param {string} [options.date] YYYY-MM-DD stamped on the archive chunk; defaults to today (UTC)
205
212
  * @param {Object} [options.fsImpl] fs implementation (tests substitute one)
206
213
  *
207
- * Not locked: concurrent callers can interleave archive appends. Call it through
214
+ * Not locked: concurrent callers can interleave archive appends, and an artifact edited between its
215
+ * read and its rewrite is detected just before the rename and left as the editor wrote it (the
216
+ * artifact is reported in `failed`; the moved block is already in the archive and a later run does
217
+ * not copy it twice), but an edit landing in the last instant before the rename is not. Call it through
208
218
  * `compactDecisions` / `runDecision` in decision-register.js, which hold the register lock.
209
219
  *
210
220
  * Every artifact is attempted. One that cannot be compacted is left as it was and reported in
@@ -8,7 +8,7 @@ const { readReadinessRecord, newerVersionOf, RECORD_VERSION } = require('./readi
8
8
  const { listCheckouts } = require('./checkouts.js');
9
9
  const { readTaskState } = require('./task-state.js');
10
10
  const { resolveActiveFeature } = require('./feature-resolve.js');
11
- const { isSafeSlug } = require('./task-scope.js');
11
+ const { isSafeSlug, flatRecordIsOurs, readRegularFileText, MAX_RECORD_FILE_BYTES } = require('./task-scope.js');
12
12
  const { REPO_ROOT } = require('../helper/repo-root.js');
13
13
 
14
14
  // The implementation gate: which stage of a workflow needs a READY readiness record before it is
@@ -19,6 +19,15 @@ const { REPO_ROOT } = require('../helper/repo-root.js');
19
19
  // orchestrator, `verify` and the edit hook (which prints the same bytes) say the same thing.
20
20
 
21
21
  const GATE = 'doflow gate readiness-before-implementation';
22
+ const RUN_RECORD_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*\.json$/;
23
+ const RUN_NAMESPACE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
24
+ /** Where a checkout keeps its run records, relative to the checkout; the one place the path is named. */
25
+ const RUN_STORE_REL = path.join('.doflow', 'state', 'orchestration');
26
+
27
+ /** The run store of the checkout at `root`. */
28
+ function runStoreDir(root ) {
29
+ return path.join(root, RUN_STORE_REL);
30
+ }
22
31
 
23
32
  /** What clears a record a newer DoFlow wrote; `readiness` here refuses to overwrite it. */
24
33
  const NEWER_NEXT = 'run this command with the DoFlow that wrote the record, or upgrade DoFlow here.';
@@ -55,13 +64,14 @@ function nextCommand({ taskId, slug, template } ) {
55
64
 
56
65
  /**
57
66
  * The refusal for one check code.
58
- * @param {'missing'|'not-ready'|'wrong-template'|'unusable'|'ambiguous'} code
67
+ * @param {'missing'|'not-ready'|'wrong-template'|'unusable'|'ambiguous'|'store-conflict'|'ambiguous-run'} code
59
68
  * @param {Object} options
60
69
  * @param {string} options.taskId
61
70
  * @param {string|null} [options.slug]
62
71
  * @param {string} options.template the template the gated stage needs
63
72
  * @param {Object|null} [options.record] the record read, for `not-ready` and `wrong-template`
64
- * @param {Array<string>} [options.candidates] the files found, for `ambiguous`
73
+ * @param {Array<string>} [options.candidates] the files found, for `ambiguous`; the worktree's then the
74
+ * main checkout's, for `store-conflict`
65
75
  * @param {string|null} [options.detail] why the record cannot be used, for `unusable`
66
76
  * @param {string|null} [options.file] the record's file, for `unusable` when a newer DoFlow wrote it
67
77
  * @param {number|null} [options.newerVersion] that newer record version; `readiness` would refuse to
@@ -86,6 +96,8 @@ function refusalText(code , { taskId, slug = null, template, record = null,
86
96
  return `${GATE}: task '${taskId}' has a readiness record that cannot be used (${detail}). ${tail}`;
87
97
  case 'ambiguous':
88
98
  return `${GATE}: task '${taskId}' has records in more than one other checkout (${candidates.join(', ')}). Next: run the command from the checkout that holds the one you mean, or run ${next} here. Nothing was changed.`;
99
+ case 'store-conflict':
100
+ return `${GATE}: task '${taskId}' has a readiness record in this worktree (${candidates[0]}) that differs from the main checkout's (${candidates[1]}). Next: keep one of the two and remove the other, then re-run. Nothing was changed.`;
89
101
  case 'ambiguous-run':
90
102
  // A readiness record written here cannot say which run is meant, so it is not offered.
91
103
  return `${GATE}: task '${taskId}' has runs in more than one other checkout (${candidates.join(', ')}). Next: run verify from the checkout that holds the run you mean. Nothing was changed.`;
@@ -168,35 +180,269 @@ function runSlugOf(run , root ) {
168
180
  return run && isSafeSlug(run.taskId) && isDir(folderOf(root, run.taskId)) ? run.taskId : null;
169
181
  }
170
182
 
183
+ /** Whether a store directory can be a run namespace rather than a lock directory. */
184
+ function isRunNamespaceDir(name ) {
185
+ return RUN_NAMESPACE_PATTERN.test(name) && !name.includes('..') && !name.endsWith('.lock');
186
+ }
187
+
188
+ /** The run record files under one checkout's store: flat `<id>.json` and `<slug>/<id>.json`, by
189
+ * path, without reading them. The one listing `findRunFile` and `orchestrationRuns` share.
190
+ * With `listing` (IC-001, the list action), a symbolic link is never followed: a linked run file is
191
+ * kept with `linked: true` so it can be reported as unreadable, and a linked namespace directory is
192
+ * not entered, so nothing outside the store root is read or named. */
193
+ function runFilesAt(root , { listing = false } = {}) {
194
+ const store = runStoreDir(root);
195
+ const files = [];
196
+ const take = (entry ) => entry.isFile() || entry.isSymbolicLink();
197
+ let entries ;
198
+ try { entries = fs.readdirSync(store, { withFileTypes: true }); } catch { return []; }
199
+ for (const entry of entries) {
200
+ if (take(entry) && RUN_RECORD_PATTERN.test(entry.name)) {
201
+ files.push({ file: path.join(store, entry.name), namespace: null, linked: entry.isSymbolicLink() });
202
+ } else if ((entry.isDirectory() || (!listing && entry.isSymbolicLink())) && isRunNamespaceDir(entry.name)) {
203
+ let nested ;
204
+ try { nested = fs.readdirSync(path.join(store, entry.name), { withFileTypes: true }); } catch { continue; }
205
+ for (const item of nested) {
206
+ if (take(item) && RUN_RECORD_PATTERN.test(item.name)) {
207
+ files.push({ file: path.join(store, entry.name, item.name), namespace: entry.name, linked: entry.isSymbolicLink() || item.isSymbolicLink() });
208
+ }
209
+ }
210
+ }
211
+ }
212
+ return files.sort((a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : 0);
213
+ }
214
+
215
+ /** The largest run file the list action and `readRunRecord` read; a bigger one is reported as `io` /
216
+ * refused (a journal is a few KiB). */
217
+ const MAX_RUN_FILE_BYTES = MAX_RECORD_FILE_BYTES;
218
+
219
+ /**
220
+ * One run's record, read the way the list action reads it (FU-16c2wz): no link is followed on the
221
+ * way from the store folder `storeDir` to the file or at the file, and the file must be a regular
222
+ * file of at most `MAX_RUN_FILE_BYTES`, so a FIFO or a device cannot block the read and a link
223
+ * cannot make it read outside the store. A run file that is not there is `null`; one that must not
224
+ * be read throws `store-unreadable` naming the file, and nothing of it is read.
225
+ * @param {Object} fsImpl
226
+ * @param {string} file the run file, under `storeDir`
227
+ * @param {string} storeDir the run store folder it belongs to
228
+ * @returns {Object|null} the parsed record
229
+ */
230
+ function readRunRecord(fsImpl , file , storeDir ) {
231
+ if (typeof fsImpl.lstatSync !== 'function' || typeof fsImpl.openSync !== 'function') return readTaskState(fsImpl, file);
232
+ const text = readRunText(fsImpl, file, storeDir);
233
+ return text === null ? null : readTaskState({ existsSync: () => true, readFileSync: () => text }, file);
234
+ }
235
+
236
+ /** `readRunRecord`'s guarded read without the parse: the run file's text, `null` when it is not
237
+ * there, `store-unreadable` when it must not be read. */
238
+ function readRunText(fsImpl , file , storeDir ) {
239
+ const relative = path.relative(storeDir, file);
240
+ const parts = relative === '' || relative.startsWith('..') || path.isAbsolute(relative) ? [] : relative.split(path.sep);
241
+ const refuse = () => {
242
+ const failure = new Error(`Run file '${file}' is not a regular file inside the run store (a link, FIFO, device or folder, or larger than ${MAX_RUN_FILE_BYTES} bytes). Nothing was read.`);
243
+ failure.code = 'store-unreadable';
244
+ throw failure;
245
+ };
246
+ let at = storeDir;
247
+ for (const part of parts.slice(0, -1)) {
248
+ at = path.join(at, part);
249
+ let stat ;
250
+ try { stat = fsImpl.lstatSync(at); } catch (error ) {
251
+ if (error.code === 'ENOENT' || error.code === 'ENOTDIR') return null;
252
+ throw error;
253
+ }
254
+ if (stat.isSymbolicLink()) refuse();
255
+ }
256
+ let stat ;
257
+ try { stat = fsImpl.lstatSync(file); } catch (error ) {
258
+ if (error.code === 'ENOENT' || error.code === 'ENOTDIR') return null;
259
+ throw error;
260
+ }
261
+ if (!stat.isFile()) refuse();
262
+ const text = readRegularFileText(file, fsImpl);
263
+ if (text === null) refuse();
264
+ return text;
265
+ }
266
+
267
+ /** The run files under one checkout's store, read, with each malformed or unreadable file isolated. */
268
+ function runEntriesAt(root , origin ) {
269
+ return runFilesAt(root, { listing: true }).map(({ file, namespace, linked }) => {
270
+ let record = null;
271
+ let error = null;
272
+ const bytes = linked ? null : readRegularFileText(file);
273
+ if (bytes === null) error = 'io';
274
+ else {
275
+ try { record = JSON.parse(bytes); } catch { error = 'unparseable'; }
276
+ }
277
+ return {
278
+ file,
279
+ root,
280
+ origin,
281
+ slug: namespace || runSlugOf(record, root),
282
+ record,
283
+ error,
284
+ };
285
+ });
286
+ }
287
+
288
+ /**
289
+ * All orchestration journals in the current store and the other checkouts, when Git can list them.
290
+ * @param {{stateRoot: string, exec?: Function}} options
291
+ * @returns {{ok: true, entries: Array<Object>}|{ok: false, error: 'cannot-list-checkouts'}}
292
+ */
293
+ function orchestrationRuns({ stateRoot, exec } ) {
294
+ const current = runEntriesAt(stateRoot, 'current');
295
+ const checkouts = listCheckouts({ cwd: stateRoot, ...(exec ? { exec } : {}) });
296
+ if (!checkouts.ok && checkouts.reason !== 'not-a-git-repository') {
297
+ return { ok: false, error: 'cannot-list-checkouts' };
298
+ }
299
+ if (!checkouts.ok || checkouts.sandbox) return { ok: true, entries: current };
300
+
301
+ const entries = [...current];
302
+ for (const root of checkouts.others) entries.push(...runEntriesAt(root, 'other'));
303
+ return { ok: true, entries };
304
+ }
305
+
306
+ /** Whether a flat record names no feature (no `featureSlug`, no `slug`): such a run is any feature's
307
+ * (`flatRecordIsOurs`), so a namespaced copy of the same id in the same store shadows it. */
308
+ function namesNoFeature(record ) {
309
+ return Boolean(record) && typeof record === 'object'
310
+ && ![record.featureSlug, record.slug].some((value) => typeof value === 'string' && value !== '');
311
+ }
312
+
313
+ /**
314
+ * IC-001/IC-003: one entry per logical run, the copy `status` reads, with `findRunFile`'s
315
+ * precedence. In one store, the namespaced `<slug>/<id>.json` wins over a flat `<id>.json` that
316
+ * names that feature (when the slug is the id, the flat file is the run's only path and wins).
317
+ * A flat file that names no feature, or that cannot be read or is a link, is keyed as `findRunFile`
318
+ * would first read it, the id as its slug, and stays its own entry. Across stores, the current
319
+ * store's copy of a key wins over every other checkout's; a current flat file `status` reads for
320
+ * any feature without a namespaced copy here (one naming no feature, or a link, which status refuses)
321
+ * also shadows the other checkouts' copies of that id. With no current copy, each other checkout
322
+ * that holds one keeps its entry (more than one is the case `status` refuses as ambiguous). A
323
+ * losing copy is dropped: `status` never reads it for this run.
324
+ * @param {Array<Object>} entries `orchestrationRuns(...).entries`
325
+ * @returns {Array<Object>} the winning entries, in input order
326
+ */
327
+ function selectRunEntries(entries ) {
328
+ const stem = (entry ) => path.basename(entry.file, '.json');
329
+ const namespaceOf = (entry ) => {
330
+ const parts = path.relative(runStoreDir(entry.root), entry.file).split(path.sep);
331
+ return parts.length > 1 ? parts[0] : null;
332
+ };
333
+ const named = (entry ) => entry.record && !namesNoFeature(entry.record) && !entry.error;
334
+ const linked = (entry ) => {
335
+ try { return fs.lstatSync(entry.file).isSymbolicLink(); } catch { return false; }
336
+ };
337
+ const keyOf = (entry ) => {
338
+ const ns = namespaceOf(entry);
339
+ if (ns !== null) return `${ns}\0${stem(entry)}`;
340
+ if (named(entry) && entry.slug) return `${entry.slug}\0${stem(entry)}`;
341
+ return `${stem(entry)}\0${stem(entry)}`;
342
+ };
343
+ const rank = (entry ) => {
344
+ const ns = namespaceOf(entry);
345
+ if (ns === null) return named(entry) && entry.slug !== stem(entry) ? 1 : 0;
346
+ return ns === stem(entry) ? 2 : 0;
347
+ };
348
+ // Ids a current flat file answers for any feature: status reads it before any other checkout.
349
+ const wildcards = new Set(entries.filter((entry) => entry.origin === 'current' && namespaceOf(entry) === null
350
+ && ((entry.record && namesNoFeature(entry.record)) || linked(entry))).map(stem));
351
+ const perRoot = new Map ();
352
+ for (const entry of entries) {
353
+ const local = `${entry.root}\0${keyOf(entry)}`;
354
+ const held = perRoot.get(local);
355
+ if (!held || rank(entry) < rank(held)) perRoot.set(local, entry);
356
+ }
357
+ const best = new Map ();
358
+ for (const entry of perRoot.values()) {
359
+ const key = keyOf(entry);
360
+ const held = best.get(key);
361
+ if (!held) best.set(key, [entry]);
362
+ else if (held[0].origin === 'current') continue;
363
+ else if (entry.origin === 'current') best.set(key, [entry]);
364
+ else held.push(entry);
365
+ }
366
+ const winners = new Set ();
367
+ for (const list of best.values()) {
368
+ for (const entry of list) {
369
+ if (entry.origin !== 'current' && wildcards.has(stem(entry))) continue;
370
+ winners.add(entry);
371
+ }
372
+ }
373
+ return entries.filter((entry) => winners.has(entry));
374
+ }
375
+
171
376
  /**
172
377
  * A task's run file: in this checkout, else in exactly one other checkout whose run belongs to the
173
- * same feature. Task ids repeat across features, so another checkout's run with the same id but
174
- * another feature (or none) is not this task's and is never read or updated from here.
378
+ * same feature. The run of a task id other than its feature's slug is `orchestration/<slug>/<id>.json`
379
+ * (115 C-1); a flat run of that id is read in its place only when it is this feature's or names none
380
+ * (D-3). Task ids repeat across features, so another checkout's run with the same id but another
381
+ * feature (or none) is not this task's and is never read or updated from here.
175
382
  * @param {{stateRoot: string, taskId: string, slug: string|null, exec?: Function}} options
176
383
  * @returns {{status: 'found'|'missing'|'ambiguous', file: string|null, root: string|null,
177
384
  * origin: 'current'|'other'|null, candidates: Array<string>}}
178
385
  */
179
- function findRunFile({ stateRoot, taskId, slug, exec } ) {
180
- const rel = path.join('.doflow', 'state', 'orchestration', `${taskId}.json`);
386
+ function findRunFile({ stateRoot, taskId, slug, exec, anyNamespace = false } ) {
387
+ const found = findRunFileFor({ stateRoot, taskId, slug, exec });
388
+ return anyNamespace && found.status === 'missing' ? findNamespacedRun({ stateRoot, taskId, exec }) : found;
389
+ }
390
+
391
+ /**
392
+ * FU-wrnnaq: the run of `taskId` under whichever feature namespace holds it, for a caller that has
393
+ * no feature to name (`status` run from another branch). This is the run the list action shows for
394
+ * the id: this checkout's namespaced run first, else the other checkouts'; exactly one is the run,
395
+ * more than one is `ambiguous` (the feature must then be named), none is `missing`. `slug` is the
396
+ * namespace the run is in, for the caller to read it by.
397
+ * @returns {{status: 'found'|'missing'|'ambiguous', file: string|null, root: string|null,
398
+ * origin: 'current'|'other'|null, candidates: Array<string>, slug?: string, byFeature?: true}}
399
+ */
400
+ function findNamespacedRun({ stateRoot, taskId, exec } ) {
401
+ const name = `${taskId}.json`;
181
402
  const missing = { status: 'missing', file: null, root: null, origin: null, candidates: [] };
182
- if (fs.existsSync(path.join(stateRoot, rel))) {
183
- return { status: 'found', file: path.join(stateRoot, rel), root: stateRoot, origin: 'current', candidates: [] };
403
+ const inRoot = (root ) => runFilesAt(root).filter((entry) => entry.namespace !== null && path.basename(entry.file) === name);
404
+ const settle = (entries , root , origin ) => (
405
+ entries.length === 1
406
+ ? { status: 'found', file: entries[0].file, root, origin, candidates: [], slug: entries[0].namespace }
407
+ : { ...missing, status: 'ambiguous', byFeature: true, candidates: entries.map((entry) => entry.file) });
408
+ const here = inRoot(stateRoot);
409
+ if (here.length > 0) return settle(here, stateRoot, 'current');
410
+ const checkouts = listCheckouts({ cwd: stateRoot, ...(exec ? { exec } : {}) });
411
+ if (!checkouts.ok || checkouts.sandbox) return missing;
412
+ const matches = checkouts.others.flatMap((root ) => inRoot(root).map((entry) => ({ ...entry, root })));
413
+ if (matches.length === 0) return missing;
414
+ return settle(matches, matches[0].root, 'other');
415
+ }
416
+
417
+ function findRunFileFor({ stateRoot, taskId, slug, exec } ) {
418
+ const store = RUN_STORE_REL;
419
+ const flat = path.join(store, `${taskId}.json`);
420
+ const namespaced = slug && slug !== taskId ? path.join(store, slug, `${taskId}.json`) : null;
421
+ const missing = { status: 'missing', file: null, root: null, origin: null, candidates: [] };
422
+ const here = runFilesAt(stateRoot);
423
+ const localNamespaced = namespaced && here.find((entry) => path.relative(stateRoot, entry.file) === namespaced);
424
+ const localFlat = here.find((entry) => path.relative(stateRoot, entry.file) === flat);
425
+ if (localNamespaced) return { status: 'found', file: localNamespaced.file, root: stateRoot, origin: 'current', candidates: [] };
426
+ if (localFlat && (!namespaced || flatRecordIsOurs(localFlat.file, slug, fs, true))) {
427
+ return { status: 'found', file: localFlat.file, root: stateRoot, origin: 'current', candidates: [] };
184
428
  }
185
429
  if (!slug) return missing;
186
430
  const checkouts = listCheckouts({ cwd: stateRoot, ...(exec ? { exec } : {}) });
187
431
  if (!checkouts.ok || checkouts.sandbox) return missing;
188
- const matches = checkouts.others.filter((root ) => {
189
- const file = path.join(root, rel);
190
- if (!fs.existsSync(file)) return false;
191
- try {
192
- return runSlugOf(JSON.parse(fs.readFileSync(file, 'utf8')), root) === slug;
193
- } catch {
194
- return false;
195
- }
196
- });
432
+ const matches = [];
433
+ for (const root of checkouts.others) {
434
+ const files = runFilesAt(root);
435
+ const found = [namespaced, flat].filter(Boolean).map((rel ) => files.find((entry) => path.relative(root, entry.file) === rel)).find((entry ) => {
436
+ if (!entry) return false;
437
+ // The same safe read as every other run read: a linked run or namespace folder, a FIFO or a
438
+ // device in another checkout is never opened, and is not a match.
439
+ try { return runSlugOf(JSON.parse(readRunText(fs, entry.file, runStoreDir(root)) ), root) === slug; } catch { return false; }
440
+ });
441
+ if (found) matches.push({ file: found.file, root });
442
+ }
197
443
  if (matches.length === 0) return missing;
198
- if (matches.length > 1) return { ...missing, status: 'ambiguous', candidates: matches.map((root ) => path.join(root, rel)) };
199
- return { status: 'found', file: path.join(matches[0], rel), root: matches[0], origin: 'other', candidates: [] };
444
+ if (matches.length > 1) return { ...missing, status: 'ambiguous', candidates: matches.map((m) => m.file) };
445
+ return { status: 'found', file: matches[0].file, root: matches[0].root, origin: 'other', candidates: [] };
200
446
  }
201
447
 
202
448
  /** The slug a refusal's next command names: one whose feature folder exists in this checkout or in
@@ -231,7 +477,7 @@ function unusableDetail(record , now ) {
231
477
  * @param {string} options.template
232
478
  * @param {Date} [options.now]
233
479
  * @param {Function} [options.exec]
234
- * @returns {{ok: boolean, code: 'ready'|'missing'|'not-ready'|'wrong-template'|'unusable'|'ambiguous',
480
+ * @returns {{ok: boolean, code: 'ready'|'missing'|'not-ready'|'wrong-template'|'unusable'|'ambiguous'|'store-conflict',
235
481
  * record: Object|null, file: string|null, origin: string|null, candidates: Array<string>,
236
482
  * detail: string|null, nextSlug: string|null, message: string|null}}
237
483
  */
@@ -254,6 +500,7 @@ function checkReadiness({ stateRoot, taskId, slug = null, template, now = new Da
254
500
  }),
255
501
  });
256
502
  if (read.status === 'ambiguous') return result('ambiguous');
503
+ if (read.status === 'conflict') return result('store-conflict');
257
504
  if (read.status === 'missing') return result('missing');
258
505
  const detail = read.status === 'unreadable' ? read.detail : unusableDetail(read.record, now);
259
506
  if (detail) return result('unusable', detail);
@@ -284,7 +531,9 @@ function verifyReadinessCheck({ stateRoot, taskId, slug = null, now = new Date()
284
531
  };
285
532
  }
286
533
  if (found.status === 'missing') return { applies: false, reason: 'no-run' };
287
- const run = readTaskState(fs, found.file);
534
+ // Reads only through the run store's safe reader; a run file that must not be read is refused by name.
535
+ const run = readRunRecord(fs, found.file, runStoreDir(found.root));
536
+ if (!run) return { applies: false, reason: 'no-run' };
288
537
  if (run.state === 'COMPLETED' || run.state === 'REJECTED') return { applies: false, reason: 'run-finished' };
289
538
  const stageNodes = (run.program || []).filter((n ) => n.type === 'stage');
290
539
  const stage = gatedStage(stageNodes);
@@ -309,6 +558,7 @@ function verifyReadinessCheck({ stateRoot, taskId, slug = null, now = new Date()
309
558
  }
310
559
 
311
560
  module.exports = {
561
+ unusableDetail,
312
562
  gatedStage,
313
563
  heldClasses,
314
564
  refusalText,
@@ -317,5 +567,10 @@ module.exports = {
317
567
  verifyReadinessCheck,
318
568
  featureSlugFor,
319
569
  runSlugOf,
570
+ orchestrationRuns,
571
+ selectRunEntries,
320
572
  findRunFile,
573
+ RUN_STORE_REL,
574
+ runStoreDir,
575
+ readRunRecord,
321
576
  };
@@ -8,7 +8,7 @@
8
8
  * States which of the report's findings the existing repair path can actually act on. That set is
9
9
  * narrower than the set of findings, and the difference is not self-evident:
10
10
  *
11
- * $ doflow status -g → 321 pending changes across 8 harnesses
11
+ * $ doflow status -g → 321 pending changes across several harnesses
12
12
  * $ doflow reconcile -g --dry-run → 10 drifts for 1 harness
13
13
  *
14
14
  * Both numbers are internally consistent. `status` reads the ledger, which records what DoFlow
@@ -162,7 +162,7 @@ function planBasis(snapshot ) {
162
162
  * source fingerprint, **and** the recorded ledger row equal to it too — which is right for its own
163
163
  * purpose, planning a write that also refreshes the row. It means a change is emitted whenever the
164
164
  * row alone is stale, and that happens routinely: the shared hooks asset projects one destination for
165
- * claude, codex, gemini, kiro and antigravity while ownership is recorded per harness, so a sibling's
165
+ * claude, codex, pi, kiro and antigravity while ownership is recorded per harness, so a sibling's
166
166
  * install legitimately refreshes bytes the other harnesses' rows still describe by their old hash.
167
167
  * Reading such a change as divergence reported one file, identical bytes, under contradictory
168
168
  * verdicts per harness, with a remedy offering to "restore" a file that was already correct.
@@ -21,7 +21,7 @@
21
21
  * first-match reading of that same order would get it backwards).
22
22
  *
23
23
  * A `merged` mode means the copies are combined rather than chosen between, so no single copy
24
- * "wins". An `unestablished` mode means the order may be documented (`gemini` documents one) or
24
+ * "wins". An `unestablished` mode means the order may be documented (no shipped harness does today) or
25
25
  * may not (`pi`, `antigravity` document neither), but either way the rule for choosing between
26
26
  * copies is not — so no winner is named. Those two cases are separate states under IC-003 and the
27
27
  * withheld reason says which: a documented order with an undocumented winner names the order it
@@ -96,7 +96,7 @@ function resolveShadowVerdict(presentScopes , precedence ) {
96
96
 
97
97
  if (mode === 'unestablished') {
98
98
  // IC-003 keeps these two states distinct: an order established with the mode explicitly
99
- // unestablished (`gemini`) is not the same finding as an order sought and found undocumented
99
+ // unestablished is not the same finding as an order sought and found undocumented
100
100
  // (`pi`, `antigravity`). Both withhold the verdict; only one of them knows the reading order.
101
101
  const orderIsEstablished = Array.isArray(order) && order.length > 0;
102
102
  return {
@@ -23,7 +23,7 @@ const path = require('node:path');
23
23
  *
24
24
  * **The bound on cost (R6).** Only the parent directories of assets already enumerated from the
25
25
  * ledger are read — one level, never recursed into. A managed tree that nests (Kiro's steering tree
26
- * mirrors `core/shared/guidance/`, `rules/` and `mcp/` included) is still covered completely
26
+ * mirrors the core set of `core/shared/guidance/`, `rules/` included) is still covered completely
27
27
  * without a walk, because every subdirectory holding a managed file is itself the parent of a
28
28
  * managed resource and so is inspected on its own account. `mcp-builder/` and `skill-creator/`
29
29
  * sitting beside a managed skills directory are never enumerated: neither is the parent of a
@@ -51,10 +51,12 @@ const path = require('node:path');
51
51
  * *every* file it copies under an `applyTo: '**'` front-matter header, so every file landing in
52
52
  * `.github/instructions/` is always applied. Asset `instructions.copilot`, native dir
53
53
  * `instructions`.
54
- * - `src/adapters/kiro/index.js` (header comment, lines 8-12) — "Kiro receives DoFlow's *full*
55
- * guidance tree as steering files", with no single-file instruction surface at all. Asset
54
+ * - `src/adapters/kiro/index.js` (header comment, lines 8-12) — Kiro has no single-file
55
+ * instruction surface and loads every steering file without front-matter. DoFlow-produced
56
+ * steering holds only the core set (selected via `guidance-manifest`), but the directory is
57
+ * still loaded wholesale, so an unmanaged file there is operative. Asset
56
58
  * `guidance.context-layer` for the `kiro` harness only, native dir `steering`; the same asset's
57
- * `../.doflow/guidance` projection for claude/codex/gemini/antigravity is name-resolved by
59
+ * `../.doflow/guidance` projection for the other harnesses is name-resolved by
58
60
  * explicit import and is not in this set.
59
61
  *
60
62
  * This is the single place to change. If the registry later declares load semantics per projection,