@khoavu882/doflow 2.0.0-beta.5 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (471) hide show
  1. package/README.md +12 -11
  2. package/bin/doflow.js +21 -11
  3. package/core/.antigravity-plugin/plugin.json +1 -2
  4. package/core/.claude-plugin/plugin.json +1 -1
  5. package/core/.codex-plugin/plugin.json +1 -1
  6. package/core/.plugin/plugin.json +1 -1
  7. package/core/harnesses/claude/hooks/lib.sh +43 -24
  8. package/core/harnesses/claude/hooks/permission-audit.sh +3 -0
  9. package/core/harnesses/claude/hooks/post-compact.sh +35 -32
  10. package/core/harnesses/claude/hooks/post-edit-lint.sh +2 -0
  11. package/core/harnesses/claude/hooks/post-shell-observe.sh +48 -0
  12. package/core/harnesses/claude/hooks/post-tool-failure.sh +3 -0
  13. package/core/harnesses/claude/hooks/pre-compact.sh +23 -27
  14. package/core/harnesses/claude/hooks/session-end.sh +23 -10
  15. package/core/harnesses/claude/hooks/skill-config-audit.sh +3 -0
  16. package/core/harnesses/claude/settings/settings.json +18 -0
  17. package/core/harnesses/claude/statusline/doflow-statusline.sh +1 -0
  18. package/core/harnesses/codex/agents/core-implementer.toml +1 -0
  19. package/core/harnesses/codex/agents/delivery-lead.toml +10 -0
  20. package/core/harnesses/codex/agents/quality-guardian.toml +1 -1
  21. package/core/harnesses/codex/agents/research-writer.toml +1 -1
  22. package/core/harnesses/codex/agents/reviewer.toml +10 -0
  23. package/core/harnesses/codex/agents/spec-analyst.toml +1 -1
  24. package/core/harnesses/codex/agents/system-architect.toml +1 -1
  25. package/core/harnesses/codex/hooks/lib.sh +44 -25
  26. package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +3 -1
  27. package/core/harnesses/codex/hooks/pre-compact.impl.sh +29 -26
  28. package/core/harnesses/codex/hooks/session-end.impl.sh +23 -10
  29. package/core/harnesses/codex/hooks/subagent-audit.sh +1 -0
  30. package/core/harnesses/copilot/hooks/doflow-lib.sh +93 -0
  31. package/core/harnesses/copilot/hooks/doflow-mcp-tool-guard.sh +9 -0
  32. package/core/harnesses/copilot/hooks/doflow-pre-bash-guard.sh +9 -0
  33. package/core/harnesses/copilot/hooks/doflow-pre-implement-gate.sh +9 -0
  34. package/core/harnesses/copilot/hooks/doflow-session-start.sh +9 -0
  35. package/core/harnesses/copilot/hooks/doflow-stop-check.sh +9 -0
  36. package/core/harnesses/copilot/hooks/doflow-subagent-start.sh +9 -0
  37. package/core/harnesses/copilot/hooks/doflow-subagent-stop.sh +9 -0
  38. package/core/harnesses/copilot/hooks/doflow.json +57 -0
  39. package/core/harnesses/kiro/agents/doflow.json +1 -1
  40. package/core/harnesses/kiro/hooks/lib.sh +26 -6
  41. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +1 -0
  42. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +1 -0
  43. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +1 -0
  44. package/core/harnesses/kiro/hooks/session-start.sh +1 -0
  45. package/core/harnesses/kiro/hooks/stop-check.sh +1 -0
  46. package/core/harnesses/pi/extensions/doflow-guard.js +8 -0
  47. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +0 -1
  48. package/core/harnesses/shared/hooks/bridge/bridge.sh +102 -9
  49. package/core/harnesses/shared/hooks/bridge/envelope.jq +87 -18
  50. package/core/harnesses/shared/hooks/bridge/observe-model.sh +75 -0
  51. package/core/harnesses/shared/hooks/bridge/respond.sh +10 -2
  52. package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +28 -14
  53. package/core/harnesses/shared/hooks/policies/capture-failure.sh +48 -15
  54. package/core/harnesses/shared/hooks/policies/compact-text.sh +46 -0
  55. package/core/harnesses/shared/hooks/policies/contract.sh +3 -0
  56. package/core/harnesses/shared/hooks/policies/deny-json.sh +2 -11
  57. package/core/harnesses/shared/hooks/policies/emission-rules.sh +37 -0
  58. package/core/harnesses/shared/hooks/policies/emission.jq +209 -0
  59. package/core/harnesses/shared/hooks/policies/emission.sh +383 -0
  60. package/core/harnesses/shared/hooks/policies/identity.sh +177 -0
  61. package/core/harnesses/shared/hooks/policies/lib.sh +63 -40
  62. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +3 -2
  63. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +213 -12
  64. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +114 -38
  65. package/core/harnesses/shared/hooks/policies/session-context.sh +30 -1
  66. package/core/harnesses/shared/hooks/policies/stop-check.sh +20 -9
  67. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +5 -2
  68. package/core/harnesses/shared/hooks/policies/summary-store.sh +347 -0
  69. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +60 -155
  70. package/core/harnesses/shared/hooks/stream-hook-runner.js +47 -8
  71. package/core/harnesses/shared/locator/doflow-run +2 -3
  72. package/core/registry/advice.json +3 -3
  73. package/core/registry/assets.json +55 -82
  74. package/core/registry/capabilities.json +12 -0
  75. package/core/registry/contracts.json +58 -31
  76. package/core/registry/contribution-points.json +32 -0
  77. package/core/registry/gates.json +32 -0
  78. package/core/registry/guidance-caps.json +40 -0
  79. package/core/registry/harnesses.json +1442 -363
  80. package/core/registry/lifecycle.json +485 -304
  81. package/core/registry/mcp.json +5 -1
  82. package/core/registry/models.json +7 -1
  83. package/core/registry/package-templates/README.md +19 -0
  84. package/core/registry/package-templates/agent/files/agent.md +9 -0
  85. package/core/registry/package-templates/agent/template.json +4 -0
  86. package/core/registry/package-templates/capability-provider/files/provider.json +9 -0
  87. package/core/registry/package-templates/capability-provider/template.json +11 -0
  88. package/core/registry/package-templates/debate-preset/files/preset.json +26 -0
  89. package/core/registry/package-templates/debate-preset/template.json +4 -0
  90. package/core/registry/package-templates/review-preset/files/preset.json +30 -0
  91. package/core/registry/package-templates/review-preset/template.json +4 -0
  92. package/core/registry/package-templates/skill/files/SKILL.md +8 -0
  93. package/core/registry/package-templates/skill/template.json +4 -0
  94. package/core/registry/package-templates/worker/files/worker.json +92 -0
  95. package/core/registry/package-templates/worker/template.json +11 -0
  96. package/core/registry/packages/doflow/doflow-package.json +15 -0
  97. package/core/registry/packages/doflow/presets/debate-3.json +26 -0
  98. package/core/registry/protocols.json +61 -0
  99. package/core/registry/routes.json +17 -0
  100. package/core/registry/rules.json +431 -0
  101. package/core/registry/settings.json +276 -0
  102. package/core/registry/storage.json +679 -31
  103. package/core/registry/telemetry.json +236 -0
  104. package/core/registry/transcripts.json +142 -0
  105. package/core/registry/verification.json +76 -1
  106. package/core/registry/workflows.json +55 -0
  107. package/core/shared/agent-specs/core-implementer.md +3 -0
  108. package/core/shared/agent-specs/delivery-lead.md +68 -0
  109. package/core/shared/agent-specs/quality-guardian.md +5 -1
  110. package/core/shared/agent-specs/research-writer.md +10 -3
  111. package/core/shared/agent-specs/reviewer.md +53 -0
  112. package/core/shared/agent-specs/spec-analyst.md +5 -6
  113. package/core/shared/agent-specs/system-architect.md +5 -1
  114. package/core/shared/guidance/DOFLOW_CORE.md +1 -5
  115. package/core/shared/guidance/modes/MODE_DeepResearch.md +1 -1
  116. package/core/shared/guidance/modes/MODE_Orchestration.md +4 -3
  117. package/core/shared/guidance/modes/MODE_Task_Management.md +14 -14
  118. package/core/shared/guidance/pointers/codex.md +1 -4
  119. package/core/shared/guidance/references/ARTIFACT_FORMAT.md +30 -16
  120. package/core/shared/guidance/references/ARTIFACT_VOICE.md +3 -2
  121. package/core/shared/guidance/references/CONSTITUTION_BASE.md +17 -15
  122. package/core/shared/guidance/references/DIAGRAM_GUIDE.md +1 -1
  123. package/core/shared/guidance/references/DOFLOW_CHAIN.md +17 -19
  124. package/core/shared/guidance/references/EVIDENCE_LEDGER.md +7 -8
  125. package/core/shared/guidance/references/MODEL_SELECTION.md +5 -3
  126. package/core/shared/guidance/references/ORCHESTRATION.md +1 -2
  127. package/core/shared/guidance/rules/RULES.md +45 -0
  128. package/core/shared/scripts/doflow/bash/do-args.sh +37 -0
  129. package/core/shared/scripts/doflow/bash/do-exec-paths.sh +4 -0
  130. package/core/shared/scripts/doflow/bash/do-git-state.sh +15 -4
  131. package/core/shared/scripts/doflow/bash/do-parallel-check.sh +4 -0
  132. package/core/shared/scripts/doflow/bash/do-paths.sh +40 -2
  133. package/core/shared/scripts/doflow/bash/do-prereqs.sh +2 -1
  134. package/core/shared/scripts/doflow/bash/do-review-package.sh +4 -0
  135. package/core/shared/scripts/doflow/bash/do-task-brief.sh +58 -16
  136. package/core/shared/scripts/doflow/bash/render-audit.sh +26 -2
  137. package/core/shared/scripts/doflow/bash/render-diagrams.sh +1 -0
  138. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -1
  139. package/core/shared/scripts/doflow/bash/validate-artifacts.sh +48 -4
  140. package/core/shared/scripts/doflow/bin/doflow-run +149 -34
  141. package/core/shared/skills/do/SKILL.md +12 -10
  142. package/core/shared/skills/do/references/advice_calls.md +26 -0
  143. package/core/shared/skills/do/references/pm_routing.md +1 -2
  144. package/core/shared/skills/do/references/program_mode.md +31 -0
  145. package/core/shared/skills/do-brainstorm/SKILL.md +20 -74
  146. package/core/shared/skills/do-brainstorm/references/handoff_branches.md +35 -0
  147. package/core/shared/skills/do-brainstorm/references/intent_seed.md +21 -0
  148. package/core/shared/skills/do-brainstorm/references/round_log.md +17 -0
  149. package/core/shared/skills/do-code-review/SKILL.md +41 -144
  150. package/core/shared/skills/do-code-review/content-types/markdown.md +24 -2
  151. package/core/shared/skills/do-code-review/references/tools.md +90 -0
  152. package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +549 -7
  153. package/core/shared/skills/do-design/SKILL.md +18 -17
  154. package/core/shared/skills/do-diagnose/SKILL.md +14 -4
  155. package/core/shared/skills/do-document/SKILL.md +11 -1
  156. package/core/shared/skills/do-document/references/deep_research.md +1 -2
  157. package/core/shared/skills/do-document/references/embedded-research.md +18 -6
  158. package/core/shared/skills/do-document/references/implementation-flow.md +5 -6
  159. package/core/shared/skills/do-execute-plan/SKILL.md +15 -58
  160. package/core/shared/skills/do-execute-plan/references/handoff_record.md +7 -2
  161. package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +13 -0
  162. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +26 -1
  163. package/core/shared/skills/do-execute-plan/references/run_position.md +13 -1
  164. package/core/shared/skills/do-execute-plan/references/scaffold.md +17 -9
  165. package/core/shared/skills/do-execute-plan/references/state_checkpoint.md +11 -0
  166. package/core/shared/skills/do-execute-plan/references/task_attempts.md +78 -24
  167. package/core/shared/skills/do-flow/SKILL.md +8 -9
  168. package/core/shared/skills/do-flow/references/task_classes.md +2 -2
  169. package/core/shared/skills/do-git/SKILL.md +4 -0
  170. package/core/shared/skills/do-implement/SKILL.md +4 -7
  171. package/core/shared/skills/do-plan/SKILL.md +26 -15
  172. package/core/shared/skills/do-test/SKILL.md +7 -4
  173. package/core/shared/templates/doflow/constitution-template.md +9 -3
  174. package/core/shared/templates/doflow/data-model-template.md +1 -1
  175. package/core/shared/templates/doflow/design-template.md +113 -16
  176. package/core/shared/templates/doflow/plan-template.md +11 -10
  177. package/core/shared/templates/doflow/question-log-template.md +14 -2
  178. package/core/shared/templates/doflow/requirement-template.md +1 -1
  179. package/core/worker-evidence/antigravity/checks.json +14 -0
  180. package/core/worker-evidence/codex/2026-10-18-cli-check.json +107 -0
  181. package/core/worker-evidence/codex/2026-10-18-live-run-edit.json +73 -0
  182. package/core/worker-evidence/codex/2026-10-18-live-run-read-only.json +62 -0
  183. package/core/worker-evidence/codex/2026-10-24-cli-check.json +126 -0
  184. package/core/worker-evidence/codex/checks.json +38 -0
  185. package/package.json +10 -6
  186. package/src/adapters/antigravity/index.js +143 -66
  187. package/src/adapters/claude/index.js +24 -4
  188. package/src/adapters/codex/agents.js +25 -15
  189. package/src/adapters/codex/index.js +48 -13
  190. package/src/adapters/codex/native-projection.js +6 -2
  191. package/src/adapters/copilot/index.js +58 -23
  192. package/src/adapters/copy-tree.js +290 -44
  193. package/src/adapters/guidance-manifest.js +71 -0
  194. package/src/adapters/guidance-pointer.js +19 -0
  195. package/src/adapters/hook-commands.js +6 -10
  196. package/src/adapters/index.js +1 -0
  197. package/src/adapters/kiro/index.js +23 -12
  198. package/src/adapters/opencode/index.js +8 -8
  199. package/src/adapters/pi/hooks.js +2 -1
  200. package/src/adapters/pi/index.js +14 -7
  201. package/src/adapters/resolve-source.js +26 -2
  202. package/src/cli/commands/install.js +2 -1
  203. package/src/cli/commands/reconcile.js +53 -9
  204. package/src/cli/commands/remove.js +2 -2
  205. package/src/cli/commands/rollback.js +6 -3
  206. package/src/cli/commands/self-update.js +18 -9
  207. package/src/cli/commands/status.js +21 -2
  208. package/src/cli/commands/ui.js +99 -0
  209. package/src/cli/commands/update.js +2 -1
  210. package/src/cli/index.js +322 -29
  211. package/src/cli/runtime-commands.js +209 -57
  212. package/src/cli/shared.js +0 -2
  213. package/src/helper/git.js +1 -1
  214. package/src/helper/guardrails.js +1 -1
  215. package/src/helper/marker-merge.js +31 -13
  216. package/src/helper/package-ids.js +37 -0
  217. package/src/helper/semver.js +94 -0
  218. package/src/install/backup.js +105 -15
  219. package/src/install/context.js +1 -1
  220. package/src/install/targets.js +31 -16
  221. package/src/lifecycle/coverage.js +20 -3
  222. package/src/lifecycle/hook-tier.js +160 -0
  223. package/src/lifecycle/index.js +412 -78
  224. package/src/lifecycle/retired-harnesses.js +318 -0
  225. package/src/lifecycle/view.js +35 -15
  226. package/src/registry/index.js +551 -26
  227. package/src/registry/worker-ids.js +45 -0
  228. package/src/release/build-package.js +0 -3
  229. package/src/release/docs-blocks.js +492 -0
  230. package/src/release/preflight.js +425 -0
  231. package/src/release/rehearse.js +401 -0
  232. package/src/release/release-workflow.js +733 -0
  233. package/src/release/sync-plugin-versions.js +43 -7
  234. package/src/runtime/advice/bench-gate.js +25 -0
  235. package/src/runtime/advice/cli.js +59 -16
  236. package/src/runtime/advice/client.js +32 -9
  237. package/src/runtime/advice/config.js +98 -133
  238. package/src/runtime/advice/doctor.js +1 -1
  239. package/src/runtime/advice/elevation.js +97 -0
  240. package/src/runtime/advice/endpoint-guard.js +9 -3
  241. package/src/runtime/advice/mode.js +37 -0
  242. package/src/runtime/advice/record.js +18 -5
  243. package/src/runtime/advice/registry.js +4 -1
  244. package/src/runtime/advice/shadow-config.js +229 -0
  245. package/src/runtime/advice/shadow-dispatcher.js +276 -0
  246. package/src/runtime/brief-rules.js +81 -0
  247. package/src/runtime/brief-tools.js +111 -34
  248. package/src/runtime/candidate.js +20 -11
  249. package/src/runtime/capabilities/executor.js +450 -0
  250. package/src/runtime/capabilities/loopback.js +41 -0
  251. package/src/runtime/capabilities/sanitizer.js +88 -0
  252. package/src/runtime/capability-router.js +135 -3
  253. package/src/runtime/checkouts.js +73 -11
  254. package/src/runtime/claims.js +13 -7
  255. package/src/runtime/cli.js +196 -19
  256. package/src/runtime/config-home.js +42 -0
  257. package/src/runtime/context/arbitrate.js +767 -0
  258. package/src/runtime/context/delivery-store.js +4 -6
  259. package/src/runtime/context/emission-rules.js +118 -0
  260. package/src/runtime/context/identity.js +227 -0
  261. package/src/runtime/context/primary-command.js +117 -0
  262. package/src/runtime/context/primary.js +344 -0
  263. package/src/runtime/context/summary-store.js +655 -0
  264. package/src/runtime/context-pack.js +154 -78
  265. package/src/runtime/decision-register.js +13 -4
  266. package/src/runtime/dispatch/admission.js +392 -0
  267. package/src/runtime/dispatch/attempt-end.js +274 -0
  268. package/src/runtime/dispatch/command.js +628 -0
  269. package/src/runtime/dispatch/controls.js +904 -0
  270. package/src/runtime/dispatch/envelope.js +276 -0
  271. package/src/runtime/dispatch/grants.js +310 -0
  272. package/src/runtime/dispatch/integrate.js +617 -0
  273. package/src/runtime/dispatch/loop.js +1835 -0
  274. package/src/runtime/dispatch/store.js +352 -0
  275. package/src/runtime/dispatch/verify-run.js +201 -0
  276. package/src/runtime/evidence-ledger.js +34 -7
  277. package/src/runtime/failure/capture.js +3 -1
  278. package/src/runtime/failure/cli.js +25 -9
  279. package/src/runtime/failure/home.js +57 -30
  280. package/src/runtime/failure/store.js +7 -2
  281. package/src/runtime/feature-pointer.js +174 -0
  282. package/src/runtime/feature-resolve.js +13 -2
  283. package/src/runtime/fleet/command.js +77 -0
  284. package/src/runtime/fleet/projects.js +178 -0
  285. package/src/runtime/fleet/tree.js +383 -0
  286. package/src/runtime/freshness.js +33 -3
  287. package/src/runtime/git-memo.js +46 -0
  288. package/src/runtime/guidance/ablation.js +427 -0
  289. package/src/runtime/health.js +565 -17
  290. package/src/runtime/history-compactor.js +13 -3
  291. package/src/runtime/implementation-gate.js +277 -22
  292. package/src/runtime/inventory/coverage.js +1 -1
  293. package/src/runtime/inventory/index.js +1 -1
  294. package/src/runtime/inventory/precedence.js +2 -2
  295. package/src/runtime/inventory/siblings.js +6 -4
  296. package/src/runtime/knowledge/code-corpus.js +277 -0
  297. package/src/runtime/knowledge/code-lexical.js +130 -0
  298. package/src/runtime/knowledge/code-retrieval.js +307 -0
  299. package/src/runtime/knowledge/code-validation.js +106 -0
  300. package/src/runtime/knowledge/gp3-gate.js +74 -0
  301. package/src/runtime/knowledge/graph-adapter.js +288 -0
  302. package/src/runtime/knowledge/graph-fallback.js +394 -0
  303. package/src/runtime/knowledge/hybrid.js +136 -8
  304. package/src/runtime/knowledge/retrieval.js +34 -1
  305. package/src/runtime/knowledge/rtk-compress.js +77 -0
  306. package/src/runtime/knowledge/semble-bridge.py +106 -0
  307. package/src/runtime/knowledge/semble-lifecycle.js +114 -0
  308. package/src/runtime/knowledge/semble-provider.js +365 -0
  309. package/src/runtime/lifecycle/cli.js +16 -6
  310. package/src/runtime/lifecycle/event-store.js +8 -6
  311. package/src/runtime/lifecycle/followup.js +6 -1
  312. package/src/runtime/lifecycle/overview.js +24 -1
  313. package/src/runtime/lifecycle/report-store.js +8 -10
  314. package/src/runtime/lifecycle/retention.js +95 -31
  315. package/src/runtime/lifecycle/root.js +9 -1
  316. package/src/runtime/lifecycle/status.js +34 -17
  317. package/src/runtime/lifecycle/store-upkeep.js +56 -7
  318. package/src/runtime/mask.js +134 -7
  319. package/src/runtime/mcp-consultations.js +40 -6
  320. package/src/runtime/model-router.js +79 -7
  321. package/src/runtime/models/catalogue.js +191 -0
  322. package/src/runtime/models/command.js +153 -0
  323. package/src/runtime/off-words.js +12 -0
  324. package/src/runtime/outcome.js +2 -2
  325. package/src/runtime/packages/command.js +1009 -0
  326. package/src/runtime/packages/contributions.js +116 -0
  327. package/src/runtime/packages/digest.js +132 -0
  328. package/src/runtime/packages/fetch.js +182 -0
  329. package/src/runtime/packages/grants.js +192 -0
  330. package/src/runtime/packages/index-file.js +134 -0
  331. package/src/runtime/packages/instruction.js +90 -0
  332. package/src/runtime/packages/manifest.js +331 -0
  333. package/src/runtime/packages/points.js +477 -0
  334. package/src/runtime/packages/programs.js +216 -0
  335. package/src/runtime/packages/scaffold.js +277 -0
  336. package/src/runtime/packages/source.js +90 -0
  337. package/src/runtime/packages/state.js +164 -0
  338. package/src/runtime/packages/store.js +449 -0
  339. package/src/runtime/packages/tar.js +106 -0
  340. package/src/runtime/packages/view.js +406 -0
  341. package/src/runtime/plan-check.js +59 -0
  342. package/src/runtime/plan-content.js +324 -0
  343. package/src/runtime/policy-init.js +118 -0
  344. package/src/runtime/profiles/doctor.js +96 -0
  345. package/src/runtime/profiles/resolve.js +244 -0
  346. package/src/runtime/profiles/rollback.js +254 -0
  347. package/src/runtime/profiles/sampler.js +41 -0
  348. package/src/runtime/readiness-record.js +16 -11
  349. package/src/runtime/readiness.js +46 -5
  350. package/src/runtime/research-request.js +50 -25
  351. package/src/runtime/retrieval-plan.js +10 -30
  352. package/src/runtime/retrieval-record.js +75 -5
  353. package/src/runtime/review/command.js +246 -0
  354. package/src/runtime/review/dominance.js +68 -0
  355. package/src/runtime/review/findings.js +434 -0
  356. package/src/runtime/review/identity.js +255 -0
  357. package/src/runtime/review/protocol-command.js +109 -0
  358. package/src/runtime/review/protocols.js +405 -0
  359. package/src/runtime/review/resolve-protocol.js +302 -0
  360. package/src/runtime/review/start.js +530 -0
  361. package/src/runtime/roadmap/facts.js +479 -0
  362. package/src/runtime/roadmap/gates.js +270 -0
  363. package/src/runtime/roadmap/loop.js +1636 -0
  364. package/src/runtime/roadmap/merge.js +381 -0
  365. package/src/runtime/roadmap/rows.js +1113 -0
  366. package/src/runtime/roadmap/seed.js +951 -0
  367. package/src/runtime/rules-report.js +336 -0
  368. package/src/runtime/scaffold/artifacts.js +60 -9
  369. package/src/runtime/scaffold/generate.js +21 -9
  370. package/src/runtime/settings/autopilot.js +106 -0
  371. package/src/runtime/settings/cli.js +280 -0
  372. package/src/runtime/settings/config-write.js +314 -0
  373. package/src/runtime/settings/files.js +271 -0
  374. package/src/runtime/settings/legacy.js +154 -0
  375. package/src/runtime/settings/lexical.js +56 -0
  376. package/src/runtime/settings/prepared-run.js +269 -0
  377. package/src/runtime/settings/project-layers.js +118 -0
  378. package/src/runtime/settings/refs.js +288 -0
  379. package/src/runtime/settings/registry.js +553 -0
  380. package/src/runtime/settings/resolve.js +713 -0
  381. package/src/runtime/settings/shared.js +37 -0
  382. package/src/runtime/settings/worker-billing.js +107 -0
  383. package/src/runtime/settings/write.js +269 -0
  384. package/src/runtime/slot-command.js +78 -0
  385. package/src/runtime/slots.js +671 -0
  386. package/src/runtime/source-version.js +1 -0
  387. package/src/runtime/state-root.js +217 -0
  388. package/src/runtime/storage/cli.js +12 -5
  389. package/src/runtime/storage/engine.js +9 -2
  390. package/src/runtime/storage/registry.js +14 -1
  391. package/src/runtime/storage/roots.js +66 -16
  392. package/src/runtime/storage/rules.js +82 -10
  393. package/src/runtime/storage/upkeep.js +10 -2
  394. package/src/runtime/storage/work-status.js +245 -26
  395. package/src/runtime/task-attempts.js +589 -69
  396. package/src/runtime/task-classifier.js +47 -11
  397. package/src/runtime/task-graph.js +373 -0
  398. package/src/runtime/task-run.js +778 -0
  399. package/src/runtime/task-scope.js +176 -55
  400. package/src/runtime/task-sources.js +147 -0
  401. package/src/runtime/task-state.js +374 -64
  402. package/src/runtime/team/admission.js +301 -0
  403. package/src/runtime/team/envelope.js +324 -0
  404. package/src/runtime/team/review-guard.js +156 -0
  405. package/src/runtime/trace/analyze.js +427 -0
  406. package/src/runtime/trace/baseline.js +770 -0
  407. package/src/runtime/trace/ledger.js +63 -140
  408. package/src/runtime/trace/otlp-export.js +568 -0
  409. package/src/runtime/trace/otlp-map.js +481 -0
  410. package/src/runtime/trace/spans.js +745 -0
  411. package/src/runtime/trace/telemetry.js +225 -0
  412. package/src/runtime/trace/views.js +1 -0
  413. package/src/runtime/transcripts/command.js +98 -0
  414. package/src/runtime/transcripts/cursor.js +326 -0
  415. package/src/runtime/transcripts/read.js +126 -0
  416. package/src/runtime/transcripts/readers/claude-jsonl.js +74 -0
  417. package/src/runtime/transcripts/readers/codex-rollout.js +70 -0
  418. package/src/runtime/transcripts/readers/copilot-events.js +90 -0
  419. package/src/runtime/transcripts/readers/kiro-jsonl.js +77 -0
  420. package/src/runtime/transcripts/readers/pi-jsonl.js +59 -0
  421. package/src/runtime/transcripts/registry.js +161 -0
  422. package/src/runtime/transcripts/stats.js +161 -0
  423. package/src/runtime/verification/check-key.js +628 -0
  424. package/src/runtime/verification/contract-runner.js +546 -11
  425. package/src/runtime/verification/engine.js +499 -9
  426. package/src/runtime/verification/impact.js +617 -0
  427. package/src/runtime/verification/inflight.js +205 -0
  428. package/src/runtime/verification/registry.js +176 -0
  429. package/src/runtime/verification/reuse.js +184 -0
  430. package/src/runtime/verification/telemetry-emit.js +254 -0
  431. package/src/runtime/verification/test-selection.js +67 -7
  432. package/src/runtime/worker/active.js +90 -0
  433. package/src/runtime/worker/cancel.js +222 -0
  434. package/src/runtime/worker/catalogue.js +122 -0
  435. package/src/runtime/worker/command.js +677 -0
  436. package/src/runtime/worker/evidence.js +288 -0
  437. package/src/runtime/worker/launch.js +288 -0
  438. package/src/runtime/worker/models.js +137 -0
  439. package/src/runtime/worker/parsers.js +218 -0
  440. package/src/runtime/worker/probes/index.js +706 -0
  441. package/src/runtime/worker/process-tree.js +415 -0
  442. package/src/runtime/worker/quota.js +43 -0
  443. package/src/runtime/worker/readiness.js +713 -0
  444. package/src/runtime/worker/run-store.js +166 -0
  445. package/src/runtime/worker/supervisor.js +541 -0
  446. package/src/runtime/worker/worktree-check.js +252 -0
  447. package/src/runtime/workflow-engine.js +5 -1
  448. package/src/runtime/workflow-orchestrator.js +578 -63
  449. package/src/runtime/worktree.js +149 -22
  450. package/src/state/index.js +1 -1
  451. package/src/state/lockfile.js +64 -10
  452. package/src/state/run-lock.js +1 -1
  453. package/types/node-commonjs.d.ts +106 -7
  454. package/core/harnesses/gemini/hooks/hooks.json +0 -77
  455. package/core/harnesses/gemini/hooks/lib.sh +0 -302
  456. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +0 -10
  457. package/core/harnesses/gemini/hooks/post-edit-lint.sh +0 -39
  458. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +0 -10
  459. package/core/harnesses/gemini/hooks/pre-compact.sh +0 -38
  460. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +0 -10
  461. package/core/harnesses/gemini/hooks/session-end.impl.sh +0 -89
  462. package/core/harnesses/gemini/hooks/session-end.sh +0 -5
  463. package/core/harnesses/gemini/hooks/session-start.sh +0 -7
  464. package/core/harnesses/gemini/settings/adapter-defaults.json +0 -4
  465. package/core/shared/guidance/FLAGS.md +0 -25
  466. package/core/shared/guidance/rules/RULE_01_SAFETY.md +0 -25
  467. package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +0 -23
  468. package/core/shared/guidance/rules/RULE_03_QUALITY.md +0 -14
  469. package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +0 -46
  470. package/src/adapters/gemini/hooks.js +0 -251
  471. package/src/adapters/gemini/index.js +0 -429
@@ -164,11 +164,33 @@ function knownServerIds(registry ) {
164
164
  return ids;
165
165
  }
166
166
 
167
+ const MANIFEST_PATTERNS = Object.freeze([
168
+ /(?:^|[\\/])package(?:-lock)?\.json$/,
169
+ /(?:^|[\\/])pnpm-lock\.yaml$/,
170
+ /(?:^|[\\/])yarn\.lock$/,
171
+ /(?:^|[\\/])Cargo\.(?:toml|lock)$/,
172
+ /(?:^|[\\/])go\.(?:mod|sum)$/,
173
+ /(?:^|[\\/])requirements.*\.txt$/,
174
+ /(?:^|[\\/])pyproject\.toml$/,
175
+ /(?:^|[\\/])poetry\.lock$/,
176
+ /(?:^|[\\/])Gemfile(?:\.lock)?$/,
177
+ /(?:^|[\\/])composer\.(?:json|lock)$/,
178
+ /(?:^|[\\/])build\.gradle(?:\.kts)?$/,
179
+ /(?:^|[\\/])pom\.xml$/,
180
+ /(?:^|[\\/])Package\.swift$/,
181
+ /(?:^|[\\/])Podfile(?:\.lock)?$/,
182
+ ]);
183
+
184
+ function touchesManifest(files ) {
185
+ if (!Array.isArray(files) || files.length === 0) return false;
186
+ return files.some((f) => MANIFEST_PATTERNS.some((pattern) => pattern.test(f)));
187
+ }
188
+
167
189
  /**
168
190
  * Servers a stage run names (IC-001 meaning). Sorted.
169
- * @param {{registry: object, skill: string, stageId?: string, taskId?: string, requests?: {list: Function}}} input
191
+ * @param {{registry: object, skill: string, stageId?: string, taskId?: string, requests?: {list: Function}, taskClass?: string, diffFiles?: string[]}} input
170
192
  */
171
- function namedServers({ registry, skill, stageId, taskId, requests } ) {
193
+ function namedServers({ registry, skill, stageId, taskId, requests, taskClass, diffFiles } ) {
172
194
  let researchRequested ;
173
195
  const hasRequest = () => {
174
196
  if (researchRequested === undefined) {
@@ -178,12 +200,21 @@ function namedServers({ registry, skill, stageId, taskId, requests } )
178
200
  }
179
201
  return researchRequested;
180
202
  };
203
+
204
+ const isDependencyChange = taskClass === 'dependency-change';
205
+ const hasManifestDiff = touchesManifest(diffFiles);
206
+
181
207
  const named = new Set ();
182
208
  for (const server of registry?.mcp || []) {
183
209
  for (const step of server.steps || []) {
184
- if (step.expectedWhen === 'research-request') {
185
- if (hasRequest()) named.add(server.id);
186
- } else if (step.skill === skill) {
210
+ if (step.skill !== skill) continue;
211
+ if (!step.expectedWhen) {
212
+ named.add(server.id);
213
+ } else if (step.expectedWhen === 'research-request' && hasRequest()) {
214
+ named.add(server.id);
215
+ } else if (step.expectedWhen === 'dependency-change' && isDependencyChange) {
216
+ named.add(server.id);
217
+ } else if (step.expectedWhen === 'manifest-review' && hasManifestDiff) {
187
218
  named.add(server.id);
188
219
  }
189
220
  }
@@ -213,13 +244,14 @@ const bySortKey = (a , b ) => (a < b ? -1 : a > b ? 1 : 0);
213
244
  function resolveConsultations({
214
245
  taskId = null, stageId = null, skill, flags, registry, ledger, requests,
215
246
  briefRecords = [], readReport, reach, addCommand, harnessName, since = null,
247
+ taskClass = null, diffFiles = [],
216
248
  } = {}) {
217
249
  const warnings = [];
218
250
  const notices = [];
219
251
  const evidenceToAdd = [];
220
252
  const registered = new Set((registry?.mcp || []).map((server ) => server.id));
221
253
  const known = knownServerIds(registry);
222
- const named = namedServers({ registry, skill, stageId, taskId, requests });
254
+ const named = namedServers({ registry, skill, stageId, taskId, requests, taskClass, diffFiles });
223
255
 
224
256
  // Rule 1: flags.
225
257
  const parsedFlags = parseMcpOutcomeFlags(flags);
@@ -408,5 +440,7 @@ module.exports = {
408
440
  parseMcpOutcomeFlags,
409
441
  parseToolsUsed,
410
442
  namedServers,
443
+ touchesManifest,
444
+ MANIFEST_PATTERNS,
411
445
  resolveConsultations,
412
446
  };
@@ -39,22 +39,58 @@ function availableProviderIds({ fsImpl = fs } = {})
39
39
  .map(([id]) => id);
40
40
  }
41
41
 
42
+ function normalizeFamilyList(value ) {
43
+ const values = Array.isArray(value) ? value : [value];
44
+ return [...new Set(values.flatMap((entry) => String(entry ?? '').split(',').map((family) => family.trim().toLowerCase()).filter(Boolean)))];
45
+ }
46
+
47
+ // families is a hand-kept stopgap; the model-facts registry (roadmap row 105) supersedes it and this field is removed then.
48
+ /** An unknown family is not evidence that a provider can review outside the author's family. */
49
+ function familyInfo(families , excludedFamilies ) {
50
+ const declared = Array.isArray(families) ? families : [];
51
+ const hasUnknown = declared.length === 0
52
+ || declared.some((family) => typeof family !== 'string' || !/^[a-z][a-z0-9-]*$/.test(family) || family === 'unknown');
53
+ const overlapsAuthor = declared.some((family) => typeof family === 'string'
54
+ && family !== 'unknown' && /^[a-z][a-z0-9-]*$/.test(family) && excludedFamilies.has(family));
55
+ const familyRelation = overlapsAuthor ? 'overlaps-author' : hasUnknown ? 'unverified' : 'cross-family';
56
+ const sameFamily = declared.length > 0 && declared.every((family) => typeof family === 'string' && excludedFamilies.has(family));
57
+ return {
58
+ families: [...declared],
59
+ familyVerified: familyRelation === 'cross-family',
60
+ familyRelation,
61
+ sameFamily,
62
+ };
63
+ }
64
+
42
65
  /**
43
66
  * Resolve one role to ranked candidates.
44
67
  * Ordering: available first, then preferred-tier match, then fallback-tier match, registry order
45
- * breaking ties. `exclude` drops provider ids entirely (e.g. the implementer when the role is
46
- * review with require:different-family) and is reported back rather than hidden.
68
+ * breaking ties. `exclude` drops provider ids entirely; `excludeFamilies` drops providers whose
69
+ * complete, known catalogue is within the excluded model families. Unknown catalogues are kept
70
+ * unverified rather than being presented as cross-family.
47
71
  * Availability unknown stays null — absence of evidence is not reported as absent.
48
72
  */
49
- function resolveModelRole({ registry, roleId, isAvailable = null, exclude = [] } = {}) {
73
+ function resolveModelRole({ registry, roleId, isAvailable = null, exclude = [], excludeFamilies } = {}) {
50
74
  const role = (registry.modelRoles || []).find((entry ) => entry.id === roleId);
51
75
  if (!role) {
52
76
  throw new Error(`Unknown model role '${roleId}'; valid: ${(registry.modelRoles || []).map((r ) => r.id).join(', ')}`);
53
77
  }
54
78
  const excluded = new Set(exclude);
79
+ const familyMode = excludeFamilies !== undefined && excludeFamilies !== null;
80
+ const normalizedFamilies = familyMode ? normalizeFamilyList(excludeFamilies) : [];
81
+ if (familyMode && normalizedFamilies.length === 0) {
82
+ throw new Error('--exclude-family requires at least one family name');
83
+ }
84
+ const excludedFamilies = new Set(normalizedFamilies);
55
85
  const candidates = [];
86
+ const familyExcluded = [];
56
87
  for (const [index, provider] of (registry.modelProviders || []).entries()) {
57
88
  if (excluded.has(provider.id)) continue;
89
+ const family = familyInfo(provider.families, excludedFamilies);
90
+ if (familyMode && family.sameFamily) {
91
+ familyExcluded.push(provider.id);
92
+ continue;
93
+ }
58
94
  const tiers = provider.tiers ?? [];
59
95
  candidates.push({
60
96
  id: provider.id,
@@ -64,6 +100,7 @@ function resolveModelRole({ registry, roleId, isAvailable = null, exclude = [] }
64
100
  available: isAvailable ? Boolean(isAvailable(provider.id)) : null,
65
101
  servesPreferredTier: role.prefer ? tiers.includes(role.prefer) : null,
66
102
  servesFallbackTier: role.fallback ? tiers.includes(role.fallback) : null,
103
+ ...(familyMode ? { families: family.families, familyRelation: family.familyRelation, familyVerified: family.familyVerified } : {}),
67
104
  _index: index,
68
105
  });
69
106
  }
@@ -79,12 +116,19 @@ function resolveModelRole({ registry, roleId, isAvailable = null, exclude = [] }
79
116
  return 0;
80
117
  });
81
118
  for (const candidate of candidates) delete candidate._index;
82
- return {
119
+ const result = {
83
120
  roleId,
84
121
  policy: { prefer: role.prefer ?? null, fallback: role.fallback ?? null, require: role.require ?? null },
85
122
  candidates,
86
123
  excluded: [...excluded],
87
124
  };
125
+ // Keep the established result shape byte-for-byte for callers that do not request family
126
+ // filtering. Family metadata is included whenever that comparison is requested.
127
+ if (familyMode) {
128
+ result.excludedFamilies = [...excludedFamilies];
129
+ result.familyExcluded = familyExcluded;
130
+ }
131
+ return result;
88
132
  }
89
133
 
90
134
  /**
@@ -118,7 +162,7 @@ function resolveRetrievalSlot({ registry, slotId, isAvailable = null } = {}
118
162
 
119
163
  /** CLI handler for `doflow model-role`. Read-only advisory routing; exits 1 on an unknown role
120
164
  * because a silently-empty candidate list would read as "no providers" rather than "bad input". */
121
- function handleModelRoleCommand({ role, exclude, json = false, repoRoot } = {}) {
165
+ function handleModelRoleCommand({ role, exclude, excludeFamilies, json = false, repoRoot } = {}) {
122
166
  if (!role) return usageError('model-role', '--role is required (one of the roles in core/registry/models.json)', json);
123
167
  const registry = (() => {
124
168
  try { return loadRegistry({ repoRoot: repoRoot || REPO_ROOT }); }
@@ -127,20 +171,48 @@ function handleModelRoleCommand({ role, exclude, json = false, repoRoot } =
127
171
  if (!registry) return undefined;
128
172
  const available = new Set(availableProviderIds());
129
173
  try {
174
+ const excludedProviderIds = Array.isArray(exclude) ? exclude : String(exclude ?? '').split(',').map((s ) => s.trim()).filter(Boolean);
175
+ const requestedFamilies = excludeFamilies === undefined || excludeFamilies === null ? null : normalizeFamilyList(excludeFamilies);
176
+ if (requestedFamilies !== null) {
177
+ const knownFamilies = [...new Set((registry.modelProviders || []).flatMap((provider ) => provider.families || []))]
178
+ .filter((family) => typeof family === 'string' && family !== 'unknown').sort();
179
+ const knownSet = new Set(knownFamilies);
180
+ const unknownFamilies = requestedFamilies.filter((family) => !knownSet.has(family));
181
+ if (requestedFamilies.length === 0) {
182
+ return usageError('model-role', `--exclude-family requires at least one family name; known families: ${knownFamilies.join(', ') || '(none)'}`, json);
183
+ }
184
+ if (requestedFamilies.includes('unknown')) {
185
+ return usageError('model-role', `unknown cannot be excluded because it names missing knowledge; known families: ${knownFamilies.join(', ') || '(none)'}`, json);
186
+ }
187
+ if (unknownFamilies.length) {
188
+ return usageError('model-role', `unknown family name${unknownFamilies.length === 1 ? '' : 's'}: ${unknownFamilies.join(', ')}; known families: ${knownFamilies.join(', ') || '(none)'}`, json);
189
+ }
190
+ }
130
191
  const resolution = resolveModelRole({
131
192
  registry,
132
193
  roleId: role,
133
194
  isAvailable: (providerId ) => available.has(providerId),
134
- exclude: Array.isArray(exclude) ? exclude : String(exclude ?? '').split(',').map((s ) => s.trim()).filter(Boolean),
195
+ exclude: excludedProviderIds,
196
+ ...(requestedFamilies === null ? {} : { excludeFamilies: requestedFamilies }),
135
197
  });
136
198
  if (json) console.log(JSON.stringify(resolution, null, 2));
137
199
  else {
138
200
  console.log(`Role '${resolution.roleId}' — prefer:${resolution.policy.prefer ?? '—'} fallback:${resolution.policy.fallback ?? '—'} require:${resolution.policy.require ?? '—'}`);
139
201
  for (const candidate of resolution.candidates) {
140
202
  const availability = candidate.available === null ? '?' : candidate.available ? 'yes' : 'no';
141
- console.log(` ${candidate.available ? '*' : ' '} ${candidate.id} (${candidate.kind}, via ${candidate.backendCli ?? 'hosted'}) available=${availability}${resolution.policy.prefer && candidate.servesPreferredTier ? ` serves ${resolution.policy.prefer}` : ''}`);
203
+ const familyStatus = requestedFamilies === null ? ''
204
+ : ` ${candidate.familyRelation}`;
205
+ console.log(` ${candidate.available ? '*' : ' '} ${candidate.id} (${candidate.kind}, via ${candidate.backendCli ?? 'hosted'}) available=${availability}${resolution.policy.prefer && candidate.servesPreferredTier ? ` serves ${resolution.policy.prefer}` : ''}${familyStatus}`);
142
206
  }
143
207
  if (resolution.excluded.length) console.log(`Excluded: ${resolution.excluded.join(', ')}`);
208
+ if (requestedFamilies !== null) {
209
+ const familySet = new Set(requestedFamilies);
210
+ const providerExcluded = new Set(excludedProviderIds);
211
+ const sameFamily = (registry.modelProviders || []).filter((provider ) => !providerExcluded.has(provider.id)
212
+ && familyInfo(provider.families, familySet).sameFamily);
213
+ for (const provider of sameFamily) console.log(` ${provider.id} same-family (excluded)`);
214
+ if (resolution.excludedFamilies.length) console.log(`Excluded families: ${resolution.excludedFamilies.join(', ')}`);
215
+ }
144
216
  }
145
217
  return finishRuntime(0);
146
218
  } catch (error ) {
@@ -0,0 +1,191 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ const { PROBES, HARNESS_IDS } = require('../worker/probes/index.js');
8
+ const { configHome } = require('../config-home.js');
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+ function getProjectCataloguePath(projectRoot ) {
48
+ return path.join(projectRoot, '.doflow', 'state', 'models', 'catalogue.json');
49
+ }
50
+
51
+ function getUserCataloguePath(homeDir , env = process.env) {
52
+ if (homeDir) {
53
+ return path.join(homeDir, '.config', 'doflow', 'state', 'models-observed.json');
54
+ }
55
+ const { dir } = configHome(env);
56
+ const baseDir = dir || path.join(os.homedir(), '.config', 'doflow');
57
+ return path.join(baseDir, 'state', 'models-observed.json');
58
+ }
59
+
60
+ function resolveProjectRoot(projectRoot , fsImpl = fs) {
61
+ if (projectRoot) return path.resolve(projectRoot);
62
+ try {
63
+ const { projectRoot: findRoot } = require('../lifecycle/root.js');
64
+ const root = findRoot(process.cwd());
65
+ if (root && fsImpl.existsSync(path.join(root, '.doflow'))) return root;
66
+ } catch {
67
+ // fallback
68
+ }
69
+ if (fsImpl.existsSync(path.join(process.cwd(), '.doflow'))) {
70
+ return process.cwd();
71
+ }
72
+ return null;
73
+ }
74
+
75
+ function readCatalogue(
76
+ projectRoot ,
77
+ options = {}
78
+ ) {
79
+ const fsImpl = options.fsImpl || fs;
80
+ const root = projectRoot ? path.resolve(projectRoot) : resolveProjectRoot(undefined, fsImpl);
81
+
82
+ if (root) {
83
+ const projPath = getProjectCataloguePath(root);
84
+ if (fsImpl.existsSync(projPath)) {
85
+ try {
86
+ const raw = fsImpl.readFileSync(projPath, 'utf8');
87
+ return JSON.parse(raw);
88
+ } catch (err ) {
89
+ if (err instanceof SyntaxError) {
90
+ const corrupt = `${projPath}.corrupt-${Date.now()}`;
91
+ try {
92
+ fsImpl.renameSync(projPath, corrupt);
93
+ } catch {
94
+ // best-effort
95
+ }
96
+ return null;
97
+ }
98
+ return null;
99
+ }
100
+ }
101
+ }
102
+
103
+ // Fallback to user cache
104
+ const userPath = getUserCataloguePath(options.homeDir, options.env);
105
+ if (fsImpl.existsSync(userPath)) {
106
+ try {
107
+ const raw = fsImpl.readFileSync(userPath, 'utf8');
108
+ return JSON.parse(raw);
109
+ } catch (err ) {
110
+ if (err instanceof SyntaxError) {
111
+ const corrupt = `${userPath}.corrupt-${Date.now()}`;
112
+ try {
113
+ fsImpl.renameSync(userPath, corrupt);
114
+ } catch {
115
+ // best-effort
116
+ }
117
+ return null;
118
+ }
119
+ return null;
120
+ }
121
+ }
122
+
123
+ return null;
124
+ }
125
+
126
+ function writeCatalogue(
127
+ catalogue ,
128
+ projectRoot ,
129
+ options = {}
130
+ ) {
131
+ const fsImpl = options.fsImpl || fs;
132
+ const root = projectRoot ? path.resolve(projectRoot) : resolveProjectRoot(undefined, fsImpl);
133
+ const targetPath = root ? getProjectCataloguePath(root) : getUserCataloguePath(options.homeDir, options.env);
134
+
135
+ const targetDir = path.dirname(targetPath);
136
+ fsImpl.mkdirSync(targetDir, { recursive: true });
137
+
138
+ const tmpPath = `${targetPath}.${process.pid}.${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`;
139
+ fsImpl.writeFileSync(tmpPath, JSON.stringify(catalogue, null, 2), 'utf8');
140
+ fsImpl.renameSync(tmpPath, targetPath);
141
+ }
142
+
143
+ async function syncHarness(
144
+ harnessId ,
145
+ options = {}
146
+ ) {
147
+ const probe = PROBES[harnessId];
148
+ if (!probe) {
149
+ return {
150
+ harnessId,
151
+ binary: harnessId,
152
+ installed: false,
153
+ version: null,
154
+ loginState: 'unsupported',
155
+ reloginHint: null,
156
+ detectedMode: 'unverified',
157
+ derivedBilling: 'unknown',
158
+ offeredModels: [],
159
+ lastSyncedAt: new Date().toISOString(),
160
+ };
161
+ }
162
+
163
+ return await probe(options);
164
+ }
165
+
166
+ async function syncAllHarnesses(
167
+ options = {}
168
+ ) {
169
+ const results = {};
170
+ for (const id of HARNESS_IDS) {
171
+ results[id] = await syncHarness(id, options);
172
+ }
173
+
174
+ const catalogue = {
175
+ version: 1,
176
+ syncedAt: new Date().toISOString(),
177
+ harnesses: results,
178
+ };
179
+
180
+ writeCatalogue(catalogue, options.projectRoot, options);
181
+ return catalogue;
182
+ }
183
+
184
+ module.exports = {
185
+ getProjectCataloguePath,
186
+ getUserCataloguePath,
187
+ readCatalogue,
188
+ writeCatalogue,
189
+ syncHarness,
190
+ syncAllHarnesses,
191
+ };
@@ -0,0 +1,153 @@
1
+ 'use strict';
2
+
3
+ const { readCatalogue, writeCatalogue, syncHarness, syncAllHarnesses } = require('./catalogue.js');
4
+ const { readAllowedModels } = require('../worker/models.js');
5
+ const { finishRuntime, usageError } = require('../cli-result.js');
6
+ const { HARNESS_IDS } = require('../worker/probes/index.js');
7
+
8
+ async function handleModelsCommand({
9
+ action,
10
+ actionGiven,
11
+ positional,
12
+ harness,
13
+ model,
14
+ json = false,
15
+ projectRoot = process.cwd(),
16
+ env = process.env,
17
+ } ) {
18
+ const act = actionGiven ? action : (action || (positional && positional[0]) || 'list');
19
+
20
+ if (act === 'sync') {
21
+ if (harness) {
22
+ if (!HARNESS_IDS.includes(harness)) {
23
+ return usageError('models', `unknown harness '${harness}'; expected one of: ${HARNESS_IDS.join(', ')}`, json);
24
+ }
25
+ const res = await syncHarness(harness, { projectRoot });
26
+ const catalogue = readCatalogue(projectRoot) || {
27
+ version: 1,
28
+ syncedAt: new Date().toISOString(),
29
+ harnesses: {},
30
+ };
31
+ catalogue.harnesses[harness] = res;
32
+ catalogue.syncedAt = new Date().toISOString();
33
+ writeCatalogue(catalogue, projectRoot);
34
+
35
+ if (json) {
36
+ console.log(JSON.stringify(res, null, 2));
37
+ } else {
38
+ const hint = res.loginState === 'logged-out' && res.reloginHint ? ` (re-login: ${res.reloginHint})` : '';
39
+ console.log(`Synced harness '${harness}': ${res.loginState}${hint}, mode: ${res.detectedMode}, billing: ${res.derivedBilling}, models: ${res.offeredModels.length}`);
40
+ }
41
+ return finishRuntime(0);
42
+ }
43
+
44
+ const catalogue = await syncAllHarnesses({ projectRoot });
45
+ if (json) {
46
+ console.log(JSON.stringify(catalogue, null, 2));
47
+ } else {
48
+ console.log(`Synced ${Object.keys(catalogue.harnesses).length} harness(es) at ${catalogue.syncedAt}`);
49
+ for (const [id, h] of Object.entries(catalogue.harnesses)) {
50
+ const hint = h.loginState === 'logged-out' && h.reloginHint ? ` (re-login: ${h.reloginHint})` : '';
51
+ console.log(` ${id.padEnd(14)} ${h.loginState.padEnd(12)} mode: ${h.detectedMode.padEnd(14)} billing: ${h.derivedBilling.padEnd(9)} models: ${h.offeredModels.length}${hint}`);
52
+ }
53
+ }
54
+ return finishRuntime(0);
55
+ }
56
+
57
+ if (act === 'list') {
58
+ const catalogue = readCatalogue(projectRoot);
59
+ const allowed = readAllowedModels({ env, projectRoot }) || [];
60
+ const rows = [];
61
+
62
+ if (catalogue && catalogue.harnesses) {
63
+ for (const [harnessId, h] of Object.entries(catalogue.harnesses)) {
64
+ for (const m of h.offeredModels || []) {
65
+ rows.push({
66
+ harness: harnessId,
67
+ model: m.id,
68
+ displayName: m.displayName || m.id,
69
+ loginState: h.loginState,
70
+ status: allowed.includes(m.id) ? 'enabled' : 'listed',
71
+ });
72
+ }
73
+ }
74
+ }
75
+
76
+ if (rows.length === 0) {
77
+ if (json) console.log(JSON.stringify({ models: [] }, null, 2));
78
+ else console.log('No observed models. Run `doflow models --action sync` to probe worker pool.');
79
+ return finishRuntime(0);
80
+ }
81
+
82
+ if (json) {
83
+ console.log(JSON.stringify(rows, null, 2));
84
+ } else {
85
+ console.log(`Observed Models (${rows.length}):`);
86
+ console.log(` ${'HARNESS'.padEnd(14)} ${'MODEL'.padEnd(28)} ${'LOGIN'.padEnd(12)} ${'STATUS'}`);
87
+ for (const row of rows) {
88
+ console.log(` ${row.harness.padEnd(14)} ${row.model.padEnd(28)} ${row.loginState.padEnd(12)} ${row.status}`);
89
+ }
90
+ }
91
+ return finishRuntime(0);
92
+ }
93
+
94
+ if (act === 'describe') {
95
+ if (!model || typeof model !== 'string') {
96
+ return usageError('models', '--model <id> is required for describe action', json);
97
+ }
98
+
99
+ const catalogue = readCatalogue(projectRoot);
100
+ const allowed = readAllowedModels({ env, projectRoot }) || [];
101
+ const matchingHarnesses = [];
102
+ let modelMeta = null;
103
+
104
+ if (catalogue && catalogue.harnesses) {
105
+ for (const [harnessId, h] of Object.entries(catalogue.harnesses)) {
106
+ const found = (h.offeredModels || []).find((m ) => m.id === model);
107
+ if (found) {
108
+ if (!modelMeta) modelMeta = found;
109
+ matchingHarnesses.push({
110
+ harnessId,
111
+ loginState: h.loginState,
112
+ detectedMode: h.detectedMode,
113
+ derivedBilling: h.derivedBilling,
114
+ });
115
+ }
116
+ }
117
+ }
118
+
119
+ if (matchingHarnesses.length === 0) {
120
+ if (json) {
121
+ console.log(JSON.stringify({ error: 'not-found', model }, null, 2));
122
+ } else {
123
+ console.error(`doflow models: model '${model}' not found in observed catalogue. Run 'doflow models --action sync' to refresh.`);
124
+ }
125
+ return finishRuntime(1);
126
+ }
127
+
128
+ const detail = {
129
+ model,
130
+ displayName: modelMeta?.displayName || model,
131
+ family: modelMeta?.family || 'unknown',
132
+ status: allowed.includes(model) ? 'enabled' : 'listed',
133
+ harnesses: matchingHarnesses,
134
+ };
135
+
136
+ if (json) {
137
+ console.log(JSON.stringify(detail, null, 2));
138
+ } else {
139
+ console.log(`Model: ${detail.model} (${detail.displayName})`);
140
+ console.log(` Family: ${detail.family}`);
141
+ console.log(` Status: ${detail.status}`);
142
+ console.log(' Offering Harnesses:');
143
+ for (const h of detail.harnesses) {
144
+ console.log(` - ${h.harnessId}: ${h.loginState} (mode: ${h.detectedMode}, billing: ${h.derivedBilling})`);
145
+ }
146
+ }
147
+ return finishRuntime(0);
148
+ }
149
+
150
+ return usageError('models', `unknown action '${act}'; expected sync, list, or describe`, json);
151
+ }
152
+
153
+ module.exports = { handleModelsCommand };
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
4
+
5
+ /** Common opt-out values used without loading the settings resolver. */
6
+ const OFF_WORDS = new Set(['off', '0', 'false', 'no']);
7
+
8
+ function isOffWord(value ) {
9
+ return typeof value === 'string' && OFF_WORDS.has(value.trim().toLowerCase());
10
+ }
11
+
12
+ module.exports = { OFF_WORDS, isOffWord };
@@ -43,7 +43,7 @@
43
43
  const fs = require('node:fs');
44
44
  const path = require('node:path');
45
45
  const { EvidenceLedger, assertSafeTaskId } = require('./evidence-ledger.js');
46
- const { taskStoreDir } = require('./task-scope.js');
46
+ const { taskStoreDir, taskRecord } = require('./task-scope.js');
47
47
  // The two verdict vocabularies, imported from the modules that own them rather than respelled
48
48
  // here. A second copy would let this file accept a readiness state readiness itself has retired.
49
49
  const { READINESS_STATES } = require('./readiness.js');
@@ -107,7 +107,7 @@ function outcomePath(projectRoot , taskId , slug = null) {
107
107
  * @returns {Object|null} the record, or null when no outcome was recorded
108
108
  */
109
109
  function readOutcome(projectRoot , taskId , slug = null) {
110
- const file = outcomePath(projectRoot, taskId, slug);
110
+ const file = taskRecord({ projectRoot, store: 'outcome', taskId: assertSafeTaskId(taskId), slug }).read;
111
111
  if (!fs.existsSync(file)) return null;
112
112
  try {
113
113
  return JSON.parse(fs.readFileSync(file, 'utf8'));