@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
@@ -0,0 +1,276 @@
1
+ 'use strict';
2
+
3
+ const { resolveShadowConfig } = require('./shadow-config.js');
4
+ const { applyElevationInvariant } = require('./elevation.js');
5
+ const { evaluateBenchBar } = require('./bench-gate.js');
6
+ const { appendPick, appendMiss, newPickId } = require('./record.js');
7
+ const { createAdviceClient } = require('./client.js');
8
+ const { loadAdviceRegistry } = require('./registry.js');
9
+ const { loadStorageRegistry } = require('../storage/registry.js');
10
+ const { resolveRoots } = require('../storage/roots.js');
11
+ const { profileAdvisorCap, capMode } = require('./mode.js');
12
+ const { REPO_ROOT } = require('../../helper/repo-root.js');
13
+
14
+ /** The advice registry's kinds (core/registry/advice.json). */
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+ /** The advice client's reply (client.ts ask). */
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+ /** One advisor call: the pick, or the reason there is none. `off` means the advice setting is off: nothing is recorded. */
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+
119
+ /** The budget of one advisor call: the registry's timeoutMs (core/registry/advice.json), 0 (no call) when unknown. */
120
+ function budgetMs(context ) {
121
+ const own = context.adviceRegistry?.timeoutMs;
122
+ if (typeof own === 'number' && Number.isFinite(own) && own > 0) return own;
123
+ try {
124
+ const loaded = loadAdviceRegistry({ repoRoot: REPO_ROOT });
125
+ const shipped = loaded.ok ? loaded.registry.timeoutMs : null;
126
+ return typeof shipped === 'number' && Number.isFinite(shipped) && shipped > 0 ? shipped : 0;
127
+ } catch {
128
+ return 0;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Asks the advisor with `model` (the one effective model; the client's own setting when null), bounded by `budget`.
134
+ * When the budget ends the call's signal is aborted, so the client drops its request, socket and timer; the bound's own
135
+ * timer is unref'd, so neither it nor a client that ignores the signal holds the process open. The client is asked
136
+ * not to record: the dispatcher records the call itself.
137
+ */
138
+ async function askAdvisor(context , services , model , budget ) {
139
+ const startedAt = Date.now();
140
+ const call = (status , reason , res ) => ({
141
+ status, pick: null, answer: null, reason, provider: res?.provider ?? null, model: res?.model ?? model, durationMs: Date.now() - startedAt,
142
+ });
143
+ let timer ;
144
+ const controller = new AbortController();
145
+ try {
146
+ const client = services.adviceClient || createAdviceClient({
147
+ adviceRegistry: context.adviceRegistry,
148
+ roots: services.roots,
149
+ fsImpl: services.fsImpl,
150
+ });
151
+ const options = {
152
+ questionSet: context.questionSet,
153
+ kind: context.kind,
154
+ staticAnswer: context.staticAnswer ?? context.staticPick,
155
+ caller: context.caller,
156
+ taskId: context.taskId ?? null,
157
+ record: false,
158
+ signal: controller.signal,
159
+ };
160
+ if (model) options.model = model;
161
+ const timeout = new Promise ((resolve) => {
162
+ timer = setTimeout(() => { controller.abort(); resolve('timeout'); }, budget);
163
+ timer.unref?.();
164
+ });
165
+ const res = await Promise.race([client.ask(options), timeout]);
166
+ if (res === 'timeout') return call('miss', 'timeout');
167
+ if (res?.status === 'off') return call('off', res.reason ?? null, res);
168
+ if (res?.status !== 'answered') return call('miss', typeof res?.reason === 'string' && res.reason ? res.reason : 'unavailable', res);
169
+ const first = res.answers?.[0];
170
+ if (typeof first?.pick !== 'string' || !first.pick) return call('miss', 'malformed-reply', res);
171
+ const asked = call('answered', null, res);
172
+ // The answer carries the model that was asked, so a caller can report it.
173
+ return { ...asked, pick: first.pick, answer: { ...first, model: asked.model } };
174
+ } catch {
175
+ return call('miss', 'config-invalid');
176
+ } finally {
177
+ clearTimeout(timer);
178
+ }
179
+ }
180
+
181
+ function storeRoots(context , services ) {
182
+ if (services.roots) return services.roots;
183
+ try {
184
+ return resolveRoots({ registry: loadStorageRegistry({ repoRoot: REPO_ROOT }), projectRoot: context.projectRoot, env: context.env });
185
+ } catch {
186
+ return null;
187
+ }
188
+ }
189
+
190
+ /** Records one advisor call through record.ts: a pick line when there is a pick, a miss line with its reason otherwise. */
191
+ function recordCall(context , services , mode , asked , pickId , elevated ) {
192
+ if (context.record === false || asked.status === 'off') return;
193
+ const roots = storeRoots(context, services);
194
+ if (!roots) return;
195
+ const common = { roots, fsImpl: services.fsImpl, kind: context.kind, caller: context.caller, model: asked.model, durationMs: asked.durationMs, mode };
196
+ if (asked.status === 'answered') {
197
+ appendPick({
198
+ ...common,
199
+ pickId,
200
+ elevated,
201
+ answer: asked.answer,
202
+ question: context.questionSet?.questions?.[0],
203
+ staticAnswer: context.staticAnswer ?? context.staticPick,
204
+ provider: asked.provider,
205
+ state: context.questionSet?.state ?? context.state,
206
+ taskId: context.taskId ?? null,
207
+ });
208
+ } else {
209
+ appendMiss({ ...common, reason: asked.reason });
210
+ }
211
+ }
212
+
213
+ async function interceptDecisionPoint(context , services = {}) {
214
+ const config = services.shadowConfig || resolveShadowConfig(context.kind, {
215
+ projectRoot: context.projectRoot,
216
+ env: context.env,
217
+ fsImpl: services.fsImpl,
218
+ roots: services.roots,
219
+ });
220
+
221
+ // A chosen rollout profile caps the configured mode (mode.ts; `staged` caps act to shadow, D2).
222
+ const mode = capMode(profileAdvisorCap({ projectRoot: context.projectRoot, env: context.env, fsImpl: services.fsImpl }), config.mode);
223
+ const staticPick = context.staticPick;
224
+ const unchanged = { kind: context.kind, effectiveDecision: staticPick, staticPick, advisorPick: null, elevated: false, agreed: null };
225
+
226
+ // Off: no model call, no record.
227
+ if (mode !== 'act' && mode !== 'shadow') return { ...unchanged, mode: 'off', pickId: null };
228
+
229
+ // The one effective model: the bar is checked for it, the client is asked with it, and the record names it.
230
+ const model = config.model;
231
+ const budget = budgetMs(context);
232
+ const pickId = newPickId();
233
+
234
+ // Shadow: return the static pick now; the detached call records its pick or miss. With record:false nothing could be
235
+ // kept, so no call is made.
236
+ const shadow = (warning ) => {
237
+ if (context.record !== false) {
238
+ askAdvisor(context, services, model, budget)
239
+ .then((asked) => recordCall(context, services, 'shadow', asked, pickId, false))
240
+ .catch(() => {});
241
+ }
242
+ return { ...unchanged, mode: 'shadow', pickId, ...(warning ? { warning } : {}) };
243
+ };
244
+ if (mode === 'shadow') return shadow();
245
+
246
+ // Act: only past the bench bar for the model that will be asked; otherwise it degrades to shadow.
247
+ let bar = { passed: false, reason: `no model is named for kind '${context.kind}'` };
248
+ if (model) {
249
+ try {
250
+ bar = await (services.benchGate || evaluateBenchBar)(context.kind, model);
251
+ } catch (error ) {
252
+ bar = { passed: false, reason: `bench gate failed: ${error?.message || 'unknown error'}` };
253
+ }
254
+ }
255
+ if (bar?.passed !== true) return shadow(`BENCH_BAR_UNMET: ${bar?.reason || 'Accuracy failed static baseline'}`);
256
+
257
+ const asked = await askAdvisor(context, services, model, budget);
258
+ const { effectiveDecision, elevated } = applyElevationInvariant(context.kind, staticPick, asked.pick);
259
+ recordCall(context, services, 'act', asked, pickId, elevated);
260
+
261
+ return {
262
+ kind: context.kind,
263
+ effectiveDecision,
264
+ mode: 'act',
265
+ staticPick,
266
+ advisorPick: asked.pick,
267
+ advisorAnswer: asked.answer,
268
+ pickId,
269
+ elevated,
270
+ agreed: asked.pick !== null ? asked.pick === staticPick : null,
271
+ };
272
+ }
273
+
274
+ module.exports = {
275
+ interceptDecisionPoint,
276
+ };
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ // The must-carry rules of a worker brief (135 item 3). Run directly by Node native type-stripping:
4
+ //
5
+ // node src/runtime/brief-rules.js --write rewrite the mustCarry key of core/registry/rules.json
6
+ // node src/runtime/brief-rules.js --check exit 0 equal, 1 differs, 2 usage
7
+ //
8
+ // RULES.md lines ending in the must-carry marker are copied into the `mustCarry` key of the rule
9
+ // ledger, because an installed runtime ships `core/registry/` but not `core/shared/`. The key is
10
+ // generated, never edited by hand; only it is rewritten, the rest of the file stays byte-identical.
11
+ // G78 fails when it differs from RULES.md.
12
+
13
+ const fs = require('node:fs');
14
+ const path = require('node:path');
15
+ const { REPO_ROOT } = require('../helper/repo-root.js');
16
+
17
+ const SOURCE_REL = 'core/shared/guidance/rules/RULES.md';
18
+ const TARGET_REL = 'core/registry/rules.json';
19
+ const MARKER = ' <!-- must-carry -->';
20
+ const MIN_LINES = 3;
21
+ const MAX_LINES = 5;
22
+
23
+ /** The marked lines of `text`, marker and leading `- ` removed, in file order. */
24
+ function mustCarryLines(text ) {
25
+ return text.split('\n')
26
+ .filter((line) => line.endsWith(MARKER))
27
+ .map((line) => line.slice(0, -MARKER.length).replace(/^- /, ''));
28
+ }
29
+
30
+ /** The ledger text with its `mustCarry` key set from `text`; throws when the marker count is out of range. */
31
+ function compileBriefRules(text , ledgerText ) {
32
+ const lines = mustCarryLines(text);
33
+ if (lines.length < MIN_LINES || lines.length > MAX_LINES) {
34
+ throw new Error(`${SOURCE_REL} has ${lines.length} must-carry lines; expected ${MIN_LINES} to ${MAX_LINES}`);
35
+ }
36
+ const ledger = JSON.parse(ledgerText);
37
+ delete ledger.mustCarry;
38
+ delete ledger.mustCarryNote;
39
+ ledger.mustCarry = lines;
40
+ ledger.mustCarryNote = `Generated from the ${MARKER.trim()} lines of ${SOURCE_REL} by src/runtime/brief-rules.js --write; never edit by hand`;
41
+ return `${JSON.stringify(ledger, null, 2)}\n`;
42
+ }
43
+
44
+ /** The committed lines under `root`, or the reason they cannot be read; never throws. */
45
+ function loadBriefRules(root ) {
46
+ try {
47
+ const data = JSON.parse(fs.readFileSync(path.join(root, TARGET_REL), 'utf8'));
48
+ if (!Array.isArray(data.mustCarry) || data.mustCarry.some((line ) => typeof line !== 'string')) return { reason: `${TARGET_REL} has no mustCarry array` };
49
+ return { lines: data.mustCarry };
50
+ } catch (err ) {
51
+ return { reason: err.code === 'ENOENT' ? `${TARGET_REL} is missing` : `${TARGET_REL} is unreadable` };
52
+ }
53
+ }
54
+
55
+ const USAGE = 'usage: node src/runtime/brief-rules.js --write|--check\n';
56
+
57
+ /** The CLI: 0 written or equal, 1 the committed file differs, 2 usage. */
58
+ function main(argv , root = REPO_ROOT) {
59
+ if (argv.length !== 1 || (argv[0] !== '--write' && argv[0] !== '--check')) {
60
+ process.stderr.write(USAGE);
61
+ return 2;
62
+ }
63
+ const target = path.join(root, TARGET_REL);
64
+ const expected = compileBriefRules(fs.readFileSync(path.join(root, SOURCE_REL), 'utf8'), fs.readFileSync(target, 'utf8'));
65
+ if (argv[0] === '--write') {
66
+ fs.writeFileSync(target, expected);
67
+ return 0;
68
+ }
69
+ if (fs.readFileSync(target, 'utf8') === expected) return 0;
70
+ process.stderr.write(`brief-rules: ${TARGET_REL} mustCarry is stale; run node src/runtime/brief-rules.js --write\n`);
71
+ return 1;
72
+ }
73
+
74
+ module.exports = { SOURCE_REL, TARGET_REL, MARKER, mustCarryLines, compileBriefRules, loadBriefRules, main };
75
+
76
+ if (require.main === module) {
77
+ try { process.exitCode = main(process.argv.slice(2)); } catch (err ) {
78
+ process.stderr.write(`brief-rules: ${err.message}\n`);
79
+ process.exitCode = 1;
80
+ }
81
+ }
@@ -17,6 +17,8 @@ const { loadRegistry } = require('../registry/index.js');
17
17
  const { CapabilityRouter } = require('./capability-router.js');
18
18
  const { probeProviders, HEALTHY, UNVERIFIED } = require('./health.js');
19
19
  const { mcpReach } = require('./mcp-reach.js');
20
+ const { touchesManifest } = require('./mcp-consultations.js');
21
+ const { loadBriefRules } = require('./brief-rules.js');
20
22
  const { WorkflowOrchestrator } = require('./workflow-orchestrator.js');
21
23
  const { resolveActiveFeature } = require('./feature-resolve.js');
22
24
  const { finishRuntime, usageError, captureCaught } = require('./cli-result.js');
@@ -28,7 +30,9 @@ const { resolveRoots } = require('./storage/roots.js');
28
30
  const { resolveSetting } = require('./advice/config.js');
29
31
  const { materializeKind } = require('./advice/kinds.js');
30
32
  const { createAdviceClient } = require('./advice/client.js');
33
+ const { resolveShadowConfig } = require('./advice/shadow-config.js');
31
34
  const { strengthWords } = require('./advice/strength.js');
35
+ const { resolveTier } = require('./worker/models.js');
32
36
 
33
37
  const SCHEMA = 1;
34
38
  const REACHABLE = new Set(['selected', 'configured']);
@@ -49,13 +53,20 @@ function hasServer(reach , harnessId , server ) {
49
53
  return harnessId ? REACHABLE.has(stateOn(reach, harnessId, server)) : reach.harnesses.some((row ) => REACHABLE.has(row.servers[server]));
50
54
  }
51
55
 
52
- /** The target harness: a registry id named by the caller, else the one harness installed at the
53
- * project scope (the global scope when the project has none), else none. */
54
- function chooseTarget({ registry, reach, harness, missing } ) {
56
+ /** IC-017: the resolved primary harness when it is a registry id and its state permits targeting. */
57
+ function primaryHarnessOf(primary , registry ) {
58
+ if (!primary || ['unknown', 'refused', 'not-primary'].includes(primary.state)) return null;
59
+ return registry.harnesses.some((item ) => item.id === primary.harness) ? primary.harness : null;
60
+ }
61
+
62
+ /** The target harness: a registry id named by the caller, else the resolved primary, else the one
63
+ * harness installed at the project scope (the global scope when the project has none), else none. */
64
+ function chooseTarget({ registry, reach, harness, primaryHarness, missing } ) {
55
65
  if (harness) {
56
66
  if (registry.harnesses.some((item ) => item.id === harness)) return harness;
57
67
  missing.push(`harness '${harness}' is not a registry harness; ignored`);
58
68
  }
69
+ if (primaryHarness) return primaryHarness;
59
70
  const rows = reach?.harnesses || [];
60
71
  for (const scope of ['project', 'global']) {
61
72
  const ids = [...new Set(rows.filter((row ) => row.scope === scope).map((row ) => row.id))];
@@ -64,22 +75,6 @@ function chooseTarget({ registry, reach, harness, missing } ) {
64
75
  return null;
65
76
  }
66
77
 
67
- /** Tier for a dispatch: the task line's tier when valid, else the owner's, else the default (IC-004).
68
- * A registry without `dispatchTiers` (the key is optional) gives `standard` with a note. */
69
- function resolveTier({ dispatchTiers, owner, tier, missing } ) {
70
- if (!dispatchTiers) {
71
- missing.push('models.json has no dispatchTiers; tier standard assumed');
72
- return { tier: 'standard', source: 'default', note: 'models.json has no dispatchTiers' };
73
- }
74
- if (tier !== undefined && tier !== null && tier !== '') {
75
- if (dispatchTiers.tiers.includes(tier)) return { tier, source: 'task' };
76
- missing.push(`tier '${tier}' is not in dispatchTiers.tiers; ignored`);
77
- }
78
- if (Object.hasOwn(dispatchTiers.byOwner, owner)) return { tier: dispatchTiers.byOwner[owner], source: 'owner' };
79
- missing.push(`owner '${owner}' has no tier in models.json dispatchTiers`);
80
- return { tier: dispatchTiers.default, source: 'default' };
81
- }
82
-
83
78
  /** The first provider of a capability whose probe status is `status`, in registry order, or null. */
84
79
  function firstWith(router , capabilityId , probes , status ) {
85
80
  return (router.capabilities[capabilityId]?.providers || []).find((provider ) => probes[provider.id]?.status === status) || null;
@@ -102,6 +97,9 @@ function resolveRoute(router , route , probes ) {
102
97
  }
103
98
 
104
99
  const cell = (text ) => String(text).replace(/\|/g, '\\|').replace(/\s+/g, ' ');
100
+ // The hand-back status ids, in the order of core/registry/workflows.json agents.handback.statuses;
101
+ // G76 holds this text, the fallback in do-task-brief.sh and every spec/TOML status line to that list.
102
+ const HANDBACK_IDS = ['done', 'done-with-concerns', 'needs-context', 'blocked'];
105
103
  const trigger = (server ) => server.trigger.replace(/\.$/, '');
106
104
 
107
105
  /**
@@ -113,16 +111,34 @@ const trigger = (server ) => server.trigger.replace(/\.$/, '');
113
111
  * @param {string} input.owner
114
112
  * @param {string} [input.tier] the task line's tier
115
113
  * @param {string} [input.harness] target harness id
114
+ * @param {Object} [input.primary] resolved primary result (IC-017)
115
+ * @param {Object} [input.bindings] workers.models bindings result (IC-017)
116
116
  * @param {2|3} [input.headingLevel]
117
+ * @param {string} [input.repoRoot] where `core/registry/rules.json` is read from
117
118
  * @param {{feature:?string, runTaskId:?string, stageId:?string, skill:?string}|null} [input.stage]
118
- * @returns {{markdown: string, record: Object}} the record is IC-018 without `tasks`
119
+ * @returns {{markdown: string, record: Object}} the IC-018 record, including model (IC-017), without `tasks`
119
120
  */
120
- function renderBriefTools({ registry, router, probes, reach, owner, tier, harness, headingLevel = 2, stage = null } ) {
121
+ function renderBriefTools({ registry, router, probes, reach, owner, tier, harness, primary = null, bindings = null, headingLevel = 2, stage = null, repoRoot = REPO_ROOT } ) {
121
122
  const missing = [];
122
123
  const hashes = '#'.repeat(Number(headingLevel) === 3 ? 3 : 2);
123
- const target = chooseTarget({ registry, reach, harness, missing });
124
+ const primaryHarness = primaryHarnessOf(primary, registry);
125
+ const target = chooseTarget({ registry, reach, harness, primaryHarness, missing });
124
126
  const targetHarness = target ? registry.harnesses.find((item ) => item.id === target) : null;
125
127
  const resolved = resolveTier({ dispatchTiers: registry.dispatchTiers, owner, tier, missing });
128
+ const lineHarness = registry.harnesses.some((item ) => item.id === harness) ? harness : primaryHarness;
129
+ const lineHarnessEntry = lineHarness ? registry.harnesses.find((item ) => item.id === lineHarness) : null;
130
+ let model = null;
131
+ if (lineHarness) {
132
+ const { resolveBinding } = require('./worker/models.js');
133
+ const binding = resolveBinding({ worker: lineHarness, tier: resolved.tier, bindings, descriptor: lineHarnessEntry?.worker });
134
+ if (binding.source === 'invalid') {
135
+ model = { harness: lineHarness, id: null, source: 'invalid' };
136
+ } else if (binding.source === 'binding') {
137
+ model = { harness: lineHarness, id: binding.model, source: 'binding' };
138
+ } else {
139
+ model = { harness: lineHarness, id: null, source: 'harness-default' };
140
+ }
141
+ }
126
142
  const sourceText = resolved.source === 'owner' ? `owner ${owner}`
127
143
  : resolved.source === 'task' ? 'task line' : `default; ${resolved.note || `owner ${owner} has no tier`}`;
128
144
 
@@ -162,8 +178,18 @@ function renderBriefTools({ registry, router, probes, reach, owner, tier, harnes
162
178
  }
163
179
 
164
180
  const skill = stage?.skill || null;
181
+ const taskClass = stage?.taskClass || null;
182
+ const diffFiles = stage?.diffFiles || [];
183
+
165
184
  const expectedServers = skill
166
- ? registry.mcp.filter((server ) => (server.steps || []).some((step ) => step.skill === skill && !step.expectedWhen)) : [];
185
+ ? registry.mcp.filter((server ) => (server.steps || []).some((step ) => {
186
+ if (step.skill !== skill) return false;
187
+ if (!step.expectedWhen) return true;
188
+ if (step.expectedWhen === 'dependency-change') return taskClass === 'dependency-change';
189
+ if (step.expectedWhen === 'manifest-review') return touchesManifest(diffFiles);
190
+ return false;
191
+ }))
192
+ : [];
167
193
  const expectedIds = new Set(expectedServers.map((server ) => server.id));
168
194
  const expected = expectedServers.map((server ) => {
169
195
  let state;
@@ -177,11 +203,20 @@ function renderBriefTools({ registry, router, probes, reach, owner, tier, harnes
177
203
  const list = (servers ) => (servers.length ? servers.map((server ) => `\`${server.id}\` (${trigger(server)})`).join('; ') : 'none');
178
204
  const targetName = targetHarness ? targetHarness.displayName || targetHarness.id : 'any installed harness';
179
205
 
206
+ const carried = loadBriefRules(repoRoot);
207
+ if ('reason' in carried) missing.push(`brief-rules: ${carried.reason}`);
208
+
180
209
  const lines = [
210
+ ...('lines' in carried ? [`${hashes} Rules that apply to you`, '', ...carried.lines.map((line ) => `- ${line}`), ''] : []),
181
211
  `${hashes} Tier and tools`,
182
212
  '',
183
213
  `Tier: \`${resolved.tier}\` (${sourceText}), per`,
184
214
  '`references/MODEL_SELECTION.md`.',
215
+ ...(model ? [model.source === 'invalid'
216
+ ? `Model: binding unreadable on \`${model.harness}\`; harness default.`
217
+ : model.source === 'binding'
218
+ ? `Model: \`${model.id}\` on \`${model.harness}\` (binding, user layer).`
219
+ : `Model: harness default on \`${model.harness}\`.`] : []),
185
220
  '',
186
221
  'Tools this machine answers with, per information need. Searching with them before reading whole',
187
222
  'files is recommended where it saves reading; it is not required.',
@@ -199,12 +234,18 @@ function renderBriefTools({ registry, router, probes, reach, owner, tier, harnes
199
234
  if (item.reach === 'unreachable') lines.push(`\`${item.server}\` is not reachable on ${targetName}; report it as \`${item.server}=unavailable\`.`);
200
235
  else if (item.reach === 'unknown') lines.push(`\`${item.server}\` may not be reachable on ${targetName}; if no tool answers, report \`${item.server}=unavailable\`.`);
201
236
  }
237
+ for (const s of expectedServers) {
238
+ if (typeof s.trigger === 'string' && s.trigger.toLowerCase().includes('library')) {
239
+ lines.push(`Vendor/library facts: \`${s.id}\` first, else a URL, else UNVERIFIED.`);
240
+ }
241
+ }
202
242
  lines.push(
203
243
  '',
204
244
  `${hashes} Report format`,
205
245
  '',
206
- `End ${Number(headingLevel) === 3 ? "each task's report" : 'your report'} with one line naming every tool and MCP server you used:`,
246
+ 'Put one line naming every tool and MCP server you used just before the final status line:',
207
247
  '`Tools used: <name>[=used|nothing-usable|unavailable|skipped:<reason>], ...`',
248
+ `End ${Number(headingLevel) === 3 ? "each task's report" : 'your report'} with one line \`Status: <status>\`, where the status is one of ${HANDBACK_IDS.map((id) => `\`${id}\``).join(', ')}.`,
208
249
  );
209
250
 
210
251
  const record = {
@@ -217,6 +258,7 @@ function renderBriefTools({ registry, router, probes, reach, owner, tier, harnes
217
258
  tier: resolved.tier,
218
259
  tierSource: resolved.source,
219
260
  harness: target,
261
+ model: model ?? null,
220
262
  expected,
221
263
  optional: also.map((server ) => server.id),
222
264
  tools,
@@ -230,7 +272,7 @@ function currentStageOf({ repoRoot = REPO_ROOT, projectRoot, taskId } ) {
230
272
  try {
231
273
  const run = new WorkflowOrchestrator({ repoRoot, projectRoot }).readRun(taskId);
232
274
  const node = run && run.cursor < run.program.length ? run.program[run.cursor] : null;
233
- return node && node.type === 'stage' ? { stageId: node.id, skill: node.skill } : null;
275
+ return node && node.type === 'stage' ? { stageId: node.id, skill: node.skill, taskClass: run.taskClass ?? null } : null;
234
276
  } catch { return null; }
235
277
  }
236
278
 
@@ -279,13 +321,14 @@ function adviceRecord(result ) {
279
321
  };
280
322
  }
281
323
  if (result?.status === 'unavailable') return { kind: 'tier', status: 'unavailable', reason: result.reason || 'config-invalid' };
324
+ if (result?.status === 'shadow') return { kind: 'tier', status: 'shadow', reason: result.reason ?? null };
282
325
  return null;
283
326
  }
284
327
 
285
328
  /**
286
329
  * The tier renderer remains synchronous/pure; optional local advice is requested afterward.
287
330
  */
288
- async function handleBriefToolsCommand({ owner, tier, harness, slug, headingLevel, tasks, json = false, repoRoot = REPO_ROOT, projectRoot = process.cwd(), buildAdapters = null, probe = probeProviders, adviceClient = null } = {}) {
331
+ async function handleBriefToolsCommand({ owner, tier, harness, slug, headingLevel, tasks, json = false, repoRoot = REPO_ROOT, projectRoot = process.cwd(), env = process.env, buildAdapters = null, probe = probeProviders, adviceClient = null } = {}) {
289
332
  if (!owner) return usageError('brief-tools', '--owner is required (the agent type the brief is for)', json);
290
333
  const level = headingLevel === undefined ? 2 : Number(headingLevel);
291
334
  if (level !== 2 && level !== 3) return usageError('brief-tools', `--heading-level must be 2 or 3, got '${headingLevel}'`, json);
@@ -297,7 +340,12 @@ async function handleBriefToolsCommand({ owner, tier, harness, slug, headingLeve
297
340
  let reach = null;
298
341
  try { reach = mcpReach({ registry, projectRoot, adapters: buildAdapters ? buildAdapters() : undefined }); } catch { notes.push('install facts unavailable; MCP reach unknown'); }
299
342
  const stage = stageFor({ repoRoot, projectRoot, slug, missing: notes });
300
- const rendered = renderBriefTools({ registry, router, probes, reach, owner, tier, harness, headingLevel: level, stage });
343
+ let primary = null;
344
+ const { readBindings } = require('./worker/models.js');
345
+ const { resolvePrimary } = require('./context/primary.js');
346
+ try { primary = resolvePrimary({ env, projectRoot, registry }); } catch { notes.push('primary unresolved; default target unchanged'); }
347
+ const bindings = readBindings({ env, projectRoot });
348
+ const rendered = renderBriefTools({ registry, router, probes, reach, owner, tier, harness, primary, bindings, headingLevel: level, stage, repoRoot });
301
349
  let markdown = rendered.markdown;
302
350
  const record = rendered.record;
303
351
  record.missing.push(...notes);
@@ -307,21 +355,50 @@ async function handleBriefToolsCommand({ owner, tier, harness, slug, headingLeve
307
355
  try {
308
356
  const adviceRegistry = loadAdviceRegistry({ repoRoot });
309
357
  const storageRegistry = loadStorageRegistry({ repoRoot });
310
- const roots = resolveRoots({ registry: storageRegistry, projectRoot: path.resolve(projectRoot) });
311
- if (adviceRegistry.ok && (adviceClient || resolveSetting({ adviceRegistry: adviceRegistry.registry, roots }).state === 'on')) {
358
+ const roots = resolveRoots({ registry: storageRegistry, projectRoot: path.resolve(projectRoot), env });
359
+ if (adviceRegistry.ok && (adviceClient || resolveSetting({ adviceRegistry: adviceRegistry.registry, roots, env }).state === 'on')) {
312
360
  const lines = taskLinesForAdvice({ projectRoot, feature: stage?.feature || slug, tasks: taskIds });
313
361
  const built = materializeKind('tier', { adviceRegistry: adviceRegistry.registry });
314
362
  const client = adviceClient || createAdviceClient({ adviceRegistry: adviceRegistry.registry, roots });
315
- const result = built.ok ? await client.ask({
316
- questionSet: { state: `${owner}\n${lines.join('\n')}`, questions: [built.question] },
317
- kind: 'tier', staticAnswer: record.tier, caller: 'brief-tools', taskId: stage?.feature || slug,
318
- }) : { status: 'unavailable', reason: built.reason || 'config-invalid' };
363
+ const state = `${owner}\n${lines.join('\n')}`;
364
+ const shadow = resolveShadowConfig('tier', { projectRoot: path.resolve(projectRoot), env, roots });
365
+ let result = { status: 'unavailable', reason: built.reason || 'config-invalid' };
366
+ if (built.ok && !shadow.kindSet) {
367
+ // advice.kinds.tier unset: the client asks and records, as before the dispatcher. A chosen rollout
368
+ // profile caps it: off asks nothing; shadow records the pick and shows none.
369
+ const { profileAdvisorCap, unsetKindPath } = require('./advice/mode.js');
370
+ const route = unsetKindPath(profileAdvisorCap({ projectRoot: path.resolve(projectRoot), env }));
371
+ if (route === 'off') result = { status: 'off', reason: null };
372
+ else {
373
+ result = await client.ask({
374
+ questionSet: { state, questions: [built.question] },
375
+ kind: 'tier', staticAnswer: record.tier, caller: 'brief-tools', taskId: stage?.feature || slug,
376
+ });
377
+ if (route === 'shadow' && result?.status === 'answered') result = { status: 'shadow', reason: null };
378
+ }
379
+ } else if (built.ok) {
380
+ const { interceptDecisionPoint } = require('./advice/shadow-dispatcher.js');
381
+ const intercepted = await interceptDecisionPoint({
382
+ kind: 'tier', caller: 'brief-tools', state, staticPick: record.tier,
383
+ questionSet: { state, questions: [built.question] }, adviceRegistry: adviceRegistry.registry,
384
+ taskId: stage?.feature || slug, projectRoot: path.resolve(projectRoot), env,
385
+ }, { adviceClient: client, roots, shadowConfig: shadow });
386
+ // Only act shows a pick, and it is the decision after the raise-only rule.
387
+ if (intercepted.mode === 'act' && intercepted.advisorPick) {
388
+ result = { status: 'answered', answers: [{
389
+ ...intercepted.advisorAnswer, pick: intercepted.effectiveDecision, pickId: intercepted.pickId,
390
+ agrees: intercepted.effectiveDecision === record.tier,
391
+ }] };
392
+ } else if (intercepted.mode === 'shadow') result = { status: 'shadow', reason: intercepted.warning || null };
393
+ else if (intercepted.mode === 'off') result = { status: 'off', reason: null };
394
+ else result = { status: 'unavailable', reason: intercepted.warning || null };
395
+ }
319
396
  const added = adviceRecord(result);
320
397
  if (added) {
321
398
  record.advice = added;
322
399
  if (added.status === 'answered') {
323
400
  const line = `Decision model: \`${added.pick}\` (${strengthWords({ word: added.strength, between: added.between })}), advisory. The tier above stands unless you change it.`;
324
- markdown = markdown.replace(/(Tier: `[^`]+`[^\n]*\n`references\/MODEL_SELECTION\.md`\.)\n\n/, `$1\n${line}\n\n`);
401
+ markdown = markdown.replace(/(Tier: `[^`]+`[^\n]*\n`references\/MODEL_SELECTION\.md`\.(?:\nModel: [^\n]*)?)\n\n/, `$1\n${line}\n\n`);
325
402
  }
326
403
  }
327
404
  }