@yemi33/minions 0.1.2447 → 0.1.2449

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 (315) hide show
  1. package/bin/cli-api-client.js +1 -1
  2. package/bin/install-internal-minions.js +1382 -44
  3. package/bin/install-layout.js +150 -0
  4. package/bin/minions.js +460 -167
  5. package/dashboard/docs/typography.md +65 -12
  6. package/dashboard/js/command-center.js +66 -7
  7. package/dashboard/js/detail-panel.js +36 -0
  8. package/dashboard/js/memory-panel.js +59 -12
  9. package/dashboard/js/qa.js +186 -45
  10. package/dashboard/js/refresh.js +148 -12
  11. package/dashboard/js/render-dispatch.js +3 -4
  12. package/dashboard/js/render-inbox.js +2 -2
  13. package/dashboard/js/render-other.js +3 -3
  14. package/dashboard/js/render-pipelines.js +14 -0
  15. package/dashboard/js/render-plans.js +57 -9
  16. package/dashboard/js/render-prd.js +132 -23
  17. package/dashboard/js/render-prs.js +195 -166
  18. package/dashboard/js/render-schedules.js +63 -3
  19. package/dashboard/js/render-utils.js +3 -3
  20. package/dashboard/js/render-watches.js +19 -3
  21. package/dashboard/js/render-work-items.js +238 -30
  22. package/dashboard/js/settings.js +205 -54
  23. package/dashboard/js/utils.js +51 -1
  24. package/dashboard/pages/home.html +1 -1
  25. package/dashboard/pages/qa.html +1 -16
  26. package/dashboard/pages/work.html +40 -0
  27. package/dashboard/shared/cc-limits.js +79 -0
  28. package/dashboard/shared/pr-filters.js +21 -38
  29. package/dashboard/shared/project-git-summary.js +1 -1
  30. package/dashboard/shared/record-filters.js +169 -0
  31. package/dashboard/shared/watches-source.js +1 -1
  32. package/dashboard/shared/welcome-popup.js +1 -1
  33. package/dashboard/shared/wi-filters.js +302 -0
  34. package/dashboard/slim/body.html +1 -0
  35. package/dashboard/slim/js/command-send.js +26 -0
  36. package/dashboard/slim/js/modals-tiles.js +380 -39
  37. package/dashboard/slim/js/status.js +13 -21
  38. package/dashboard/slim/layout.html +1 -0
  39. package/dashboard/slim/panel-bootstrap.js +6 -2
  40. package/dashboard/slim/styles.css +38 -0
  41. package/dashboard/styles.css +159 -55
  42. package/dashboard-build.js +52 -6
  43. package/dashboard.js +956 -423
  44. package/docs/README.md +11 -6
  45. package/docs/api-errors.md +2 -2
  46. package/docs/architecture-review-2026-07-09.md +1 -1
  47. package/docs/architecture.excalidraw +2 -2
  48. package/docs/auto-discovery.md +18 -9
  49. package/docs/branch-derivation.md +4 -4
  50. package/docs/capture-demos.js +39 -2
  51. package/docs/ci-runner-canary.md +123 -0
  52. package/docs/claude-md-propagation.md +2 -2
  53. package/docs/cloud-agent-dispatch.md +204 -0
  54. package/docs/command-center.md +7 -7
  55. package/docs/completion-reports.md +43 -20
  56. package/docs/constants.md +10 -3
  57. package/docs/constellation-bridge.md +134 -6
  58. package/docs/constellation-style-telemetry.md +4 -4
  59. package/docs/contracts/capability-protocol.v1.json +165 -0
  60. package/docs/cooldown-merge-semantics.md +12 -12
  61. package/docs/copilot-cli-schema.md +7 -7
  62. package/docs/cross-repo-plans.md +10 -10
  63. package/docs/dead-code-audit-retractions.md +5 -5
  64. package/docs/default-branch-ci.md +173 -0
  65. package/docs/deprecated.json +31 -31
  66. package/docs/design-inbox-entries-schema.md +3 -3
  67. package/docs/design-language.md +1051 -0
  68. package/docs/design-state-storage.md +11 -11
  69. package/docs/diagnostics-crash-reports.md +9 -9
  70. package/docs/diagnostics-memory.md +5 -5
  71. package/docs/documentation-audit-2026-07-09.md +7 -7
  72. package/docs/engine-restart.md +90 -5
  73. package/docs/harness-mode.md +1 -1
  74. package/docs/internal-install.md +338 -39
  75. package/docs/kb-dedup-duplicate-pair-investigation.md +5 -5
  76. package/docs/kb-pr3223-cascade-archiving.md +1 -1
  77. package/docs/kb-pr696-merge-conflict-docs.md +6 -6
  78. package/docs/kb-sweep.md +35 -35
  79. package/docs/keep-processes.md +1 -1
  80. package/docs/live-checkout-mode.md +30 -30
  81. package/docs/managed-spawn.md +18 -14
  82. package/docs/named-agents.md +7 -7
  83. package/docs/plan-lifecycle.md +69 -2
  84. package/docs/pr-author-identity.md +114 -0
  85. package/docs/pr-auto-fix-dispatch.md +19 -4
  86. package/docs/pr-comment-followup.md +6 -6
  87. package/docs/pr-review-fix-loop.md +59 -10
  88. package/docs/process-termination.md +40 -0
  89. package/docs/proposals/repo-pool-for-live-checkout.md +13 -13
  90. package/docs/qa-runbook-lifecycle.md +367 -17
  91. package/docs/qa-runbooks.md +3 -3
  92. package/docs/rfc-completion-json.md +18 -18
  93. package/docs/runtime-adapters.md +26 -21
  94. package/docs/security.md +6 -6
  95. package/docs/self-improvement.md +4 -4
  96. package/docs/shared-lifecycle-module-map.md +473 -472
  97. package/docs/skills.md +52 -3
  98. package/docs/slim-ux/concepts.md +121 -116
  99. package/docs/specs/agent-configurability.md +18 -18
  100. package/docs/specs/agent-rename.md +18 -18
  101. package/docs/team-memory.md +38 -21
  102. package/docs/timeouts-and-liveness.md +118 -10
  103. package/docs/tutorials/01-install-and-connect.md +1 -1
  104. package/docs/watches.md +40 -39
  105. package/docs/workspace-manifests.md +4 -4
  106. package/docs/worktree-lifecycle.md +293 -14
  107. package/engine/README.md +46 -0
  108. package/engine/{ado-comment.js → ado/comment.js} +8 -8
  109. package/engine/{ado-git-auth.js → ado/git-auth.js} +4 -4
  110. package/engine/{ado.js → ado/index.js} +417 -63
  111. package/engine/{ado-status.js → ado/status.js} +6 -8
  112. package/engine/{ado-token.js → ado/token.js} +1 -1
  113. package/engine/{acp-transport.js → agents/acp-transport.js} +62 -22
  114. package/engine/{agent-worker-pool.js → agents/agent-worker-pool.js} +17 -8
  115. package/engine/{cc-worker-pool.js → agents/cc-worker-pool.js} +16 -6
  116. package/engine/{claude-md-context.js → agents/claude-md-context.js} +5 -5
  117. package/engine/{harness-context.js → agents/harness-context.js} +5 -5
  118. package/engine/{harness.js → agents/harness.js} +3 -3
  119. package/engine/{llm.js → agents/llm.js} +18 -14
  120. package/engine/{model-discovery.js → agents/model-discovery.js} +2 -2
  121. package/engine/{playbook.js → agents/playbook.js} +155 -22
  122. package/engine/{pooled-agent-process.js → agents/pooled-agent-process.js} +14 -12
  123. package/engine/{preflight.js → agents/preflight.js} +29 -10
  124. package/engine/{spawn-agent.js → agents/spawn-agent.js} +25 -14
  125. package/engine/{spawn-phase-watchdog.js → agents/spawn-phase-watchdog.js} +16 -7
  126. package/engine/{steering.js → agents/steering.js} +5 -5
  127. package/engine/{tools-inventory.js → agents/tools-inventory.js} +2 -2
  128. package/engine/{agent-api-validation.js → api/agent-api-validation.js} +2 -2
  129. package/engine/{api-validation.js → api/api-validation.js} +1 -1
  130. package/engine/api/bridge.js +787 -0
  131. package/engine/{cc-api-validation.js → api/cc-api-validation.js} +1 -1
  132. package/engine/api/companion.js +560 -0
  133. package/engine/{content-api-validation.js → api/content-api-validation.js} +2 -2
  134. package/engine/{pr-issue-validation.js → api/pr-issue-validation.js} +33 -6
  135. package/engine/{settings-validation.js → api/settings-validation.js} +32 -4
  136. package/engine/api-contracts/agent-content.js +4 -4
  137. package/engine/api-contracts/capability-manifest.js +236 -0
  138. package/engine/api-contracts/capability-protocol.js +333 -0
  139. package/engine/api-contracts/cc-ops.js +1 -1
  140. package/engine/api-contracts/config-runtime.js +5 -0
  141. package/engine/api-contracts/core.js +28 -1
  142. package/engine/api-contracts/index.js +100 -0
  143. package/engine/api-contracts/orchestration.js +18 -5
  144. package/engine/api-contracts/pull-requests.js +37 -6
  145. package/engine/api-contracts/qa-process.js +29 -6
  146. package/engine/api-contracts/work-plan-prd.js +21 -1
  147. package/engine/cloud/contract.js +212 -0
  148. package/engine/cloud/index.js +159 -0
  149. package/engine/{execution-model.js → core/execution-model.js} +1 -1
  150. package/engine/{features.js → core/features.js} +4 -4
  151. package/engine/{operator-identity.js → core/operator-identity.js} +1 -1
  152. package/engine/{queries.js → core/queries.js} +201 -36
  153. package/engine/{safe-expr.js → core/safe-expr.js} +1 -1
  154. package/engine/{shared.js → core/shared.js} +1637 -175
  155. package/engine/{stdio-timestamps.js → core/stdio-timestamps.js} +1 -1
  156. package/engine/{untrusted-fence.js → core/untrusted-fence.js} +3 -3
  157. package/engine/db/index.js +11 -2
  158. package/engine/db/migrations/002-dispatches.js +3 -3
  159. package/engine/db/migrations/003-work-items.js +1 -1
  160. package/engine/db/migrations/004-pull-requests.js +1 -1
  161. package/engine/db/migrations/006-metrics.js +1 -1
  162. package/engine/db/migrations/007-watches.js +2 -2
  163. package/engine/db/migrations/008-small-state.js +1 -1
  164. package/engine/db/migrations/009-qa.js +1 -1
  165. package/engine/db/migrations/010-pr-links.js +1 -1
  166. package/engine/db/migrations/011-remaining-state.js +1 -1
  167. package/engine/db/migrations/012-steering-deliveries.js +2 -2
  168. package/engine/db/migrations/013-backfill-broken-note-links.js +1 -1
  169. package/engine/db/migrations/014-pr-fix-target-prefs.js +2 -2
  170. package/engine/db/migrations/015-plans-prds.js +0 -0
  171. package/engine/db/migrations/018-sql-only-cutover.js +2 -2
  172. package/engine/db/migrations/021-archived-work-items.js +1 -1
  173. package/engine/db/migrations/022-global-cc-session.js +1 -1
  174. package/engine/db/migrations/023-engine-state.js +1 -1
  175. package/engine/db/migrations/025-malformed-work-item-phantoms.js +1 -1
  176. package/engine/db/migrations/027-review-learning-lifecycle.js +1 -1
  177. package/engine/db/migrations/029-repair-reused-versions.js +20 -0
  178. package/engine/db/migrations/031-pr-author-identity.js +137 -0
  179. package/engine/{consolidation.js → memory/consolidation.js} +6 -6
  180. package/engine/{kb-sweep-runner.js → memory/kb-sweep-runner.js} +2 -2
  181. package/engine/{kb-sweep.js → memory/kb-sweep.js} +9 -7
  182. package/engine/{memory-retrieval.js → memory/memory-retrieval.js} +46 -4
  183. package/engine/{memory-store.js → memory/memory-store.js} +3 -3
  184. package/engine/{promotion.js → memory/promotion.js} +3 -3
  185. package/engine/{review-learning-backfill.js → memory/review-learning-backfill.js} +6 -6
  186. package/engine/{review-learning.js → memory/review-learning.js} +10 -5
  187. package/engine/{diagnostics-memory.js → observability/diagnostics-memory.js} +1 -1
  188. package/engine/{logs-store.js → observability/logs-store.js} +5 -5
  189. package/engine/{metrics-store.js → observability/metrics-store.js} +4 -4
  190. package/engine/{check-status.js → operations/check-status.js} +3 -3
  191. package/engine/{cli.js → operations/cli.js} +271 -113
  192. package/engine/{distribution.js → operations/distribution.js} +5 -6
  193. package/engine/{cleanup.js → orchestration/cleanup.js} +72 -45
  194. package/engine/{cooldown.js → orchestration/cooldown.js} +5 -5
  195. package/engine/{dispatch-events.js → orchestration/dispatch-events.js} +2 -2
  196. package/engine/{dispatch.js → orchestration/dispatch.js} +129 -36
  197. package/engine/orchestration/failed-scheduled-cleanup.js +274 -0
  198. package/engine/{lifecycle.js → orchestration/lifecycle.js} +198 -90
  199. package/engine/{meeting.js → orchestration/meeting.js} +6 -16
  200. package/engine/{pipeline.js → orchestration/pipeline.js} +12 -12
  201. package/engine/{pre-dispatch-eval.js → orchestration/pre-dispatch-eval.js} +10 -9
  202. package/engine/{routing.js → orchestration/routing.js} +3 -3
  203. package/engine/{schedule-bootstrap.js → orchestration/schedule-bootstrap.js} +4 -4
  204. package/engine/{scheduler.js → orchestration/scheduler.js} +38 -8
  205. package/engine/{timeout.js → orchestration/timeout.js} +158 -109
  206. package/engine/{db-events.js → persistence/db-events.js} +2 -2
  207. package/engine/{dispatch-store.js → persistence/dispatch-store.js} +7 -7
  208. package/engine/{inbox-store.js → persistence/inbox-store.js} +2 -2
  209. package/engine/{note-link-backfill.js → persistence/note-link-backfill.js} +4 -4
  210. package/engine/{pr-fix-target-store.js → persistence/pr-fix-target-store.js} +8 -8
  211. package/engine/{pull-requests-store.js → persistence/pull-requests-store.js} +21 -7
  212. package/engine/{small-state-store.js → persistence/small-state-store.js} +31 -31
  213. package/engine/persistence/state-operations.js +350 -0
  214. package/engine/{steering-store.js → persistence/steering-store.js} +6 -6
  215. package/engine/{issues.js → planning/issues.js} +2 -2
  216. package/engine/{plan-prd-validation.js → planning/plan-prd-validation.js} +8 -2
  217. package/engine/planning/prd-result-sidecar.js +190 -0
  218. package/engine/{prd-store.js → planning/prd-store.js} +17 -17
  219. package/engine/{project-discovery.js → planning/project-discovery.js} +5 -5
  220. package/engine/{projects.js → planning/projects.js} +10 -10
  221. package/engine/{resolve-area.js → planning/resolve-area.js} +1 -1
  222. package/engine/{work-item-validation.js → planning/work-item-validation.js} +39 -3
  223. package/engine/{work-items-store.js → planning/work-items-store.js} +29 -21
  224. package/engine/{keep-process-sweep.js → processes/keep-process-sweep.js} +57 -17
  225. package/engine/{managed-spawn-launcher.js → processes/managed-spawn-launcher.js} +3 -3
  226. package/engine/{managed-spawn.js → processes/managed-spawn.js} +97 -46
  227. package/engine/{process-utils.js → processes/process-utils.js} +599 -55
  228. package/engine/{abandoned-pr-reconciliation.js → providers/abandoned-pr-reconciliation.js} +17 -7
  229. package/engine/{comment-classifier.js → providers/comment-classifier.js} +85 -17
  230. package/engine/{comment-format.js → providers/comment-format.js} +5 -5
  231. package/engine/{gh-comment.js → providers/gh-comment.js} +15 -15
  232. package/engine/{gh-token.js → providers/gh-token.js} +4 -4
  233. package/engine/{github.js → providers/github.js} +131 -54
  234. package/engine/{pr-action.js → providers/pr-action.js} +13 -12
  235. package/engine/{pr-clone-keep.js → providers/pr-clone-keep.js} +7 -7
  236. package/engine/{pr-devbox.js → providers/pr-devbox.js} +6 -6
  237. package/engine/{pr-fix-target.js → providers/pr-fix-target.js} +13 -13
  238. package/engine/{pr-remote-patch.js → providers/pr-remote-patch.js} +4 -4
  239. package/engine/{pr-resolve.js → providers/pr-resolve.js} +7 -7
  240. package/engine/{pr-temp-clone.js → providers/pr-temp-clone.js} +5 -5
  241. package/engine/{pr-track.js → providers/pr-track.js} +11 -13
  242. package/engine/{shared-branch-pr-reconcile.js → providers/shared-branch-pr-reconcile.js} +4 -4
  243. package/engine/qa/auto-prd-qa.js +313 -0
  244. package/engine/{qa-from-prd.js → qa/from-prd.js} +42 -12
  245. package/engine/qa/prd-session.js +240 -0
  246. package/engine/{qa-process-validation.js → qa/process-validation.js} +14 -9
  247. package/engine/{qa-runbooks.js → qa/runbooks.js} +1 -1
  248. package/engine/{qa-runs.js → qa/runs.js} +286 -15
  249. package/engine/{qa-sessions.js → qa/sessions.js} +595 -49
  250. package/engine/qa/visual-journey.js +654 -0
  251. package/engine/{qa-runners.js → qa-runners/index.js} +7 -7
  252. package/engine/qa-runners/maestro.js +3 -3
  253. package/engine/qa-runners/playwright.js +2 -2
  254. package/engine/{restart-health.js → recovery/restart-health.js} +48 -4
  255. package/engine/recovery/stop-stack.js +607 -0
  256. package/engine/{supervisor.js → recovery/supervisor.js} +105 -175
  257. package/engine/{watchdog.js → recovery/watchdog.js} +136 -13
  258. package/engine/runtimes/claude.js +14 -12
  259. package/engine/runtimes/codex.js +8 -6
  260. package/engine/runtimes/copilot.js +17 -16
  261. package/engine/{watch-actions.js → watches/actions.js} +13 -13
  262. package/engine/{watches.js → watches/index.js} +43 -32
  263. package/engine/{watches-store.js → watches/store.js} +4 -4
  264. package/engine/{create-pr-worktree.js → worktrees/create-pr.js} +1 -1
  265. package/engine/{worktree-gc.js → worktrees/gc.js} +70 -22
  266. package/engine/worktrees/inventory.js +671 -0
  267. package/engine/{live-checkout.js → worktrees/live-checkout.js} +4 -4
  268. package/engine/{worktree-pool.js → worktrees/pool.js} +2 -2
  269. package/engine/{worktree-preflight.js → worktrees/preflight.js} +1 -0
  270. package/engine/worktrees/quarantine-refs.js +173 -0
  271. package/engine.js +1137 -208
  272. package/minions.js +147 -77
  273. package/package.json +10 -6
  274. package/playbooks/_pr-description-audit.md +110 -78
  275. package/playbooks/build-fix-complex.md +2 -0
  276. package/playbooks/fix.md +16 -12
  277. package/playbooks/implement-shared.md +2 -0
  278. package/playbooks/implement.md +19 -20
  279. package/playbooks/plan-to-prd.md +18 -3
  280. package/playbooks/qa-session-draft.md +136 -1
  281. package/playbooks/qa-session-execute.md +80 -2
  282. package/playbooks/qa-session-setup.md +17 -1
  283. package/playbooks/qa-validate.md +1 -1
  284. package/playbooks/setup.md +2 -0
  285. package/playbooks/shared-rules.md +25 -32
  286. package/playbooks/templates/followup-dispatch.md +4 -3
  287. package/playbooks/verify.md +1 -1
  288. package/prompts/cc-system.md +19 -27
  289. package/watch-plugins/README.md +92 -0
  290. package/watch-plugins/ado-author-prs.js +336 -0
  291. package/watch-plugins/gh-author-prs.js +375 -0
  292. package/watch-plugins/http.js +474 -0
  293. package/watch-plugins/teams-channel.js +869 -0
  294. package/docs/dev-composite-workflow.md +0 -101
  295. package/docs/pr-screenshots/pr-886/after-single-header.png +0 -0
  296. package/docs/pr-screenshots/pr-886/before-duplicate-header.png +0 -0
  297. package/docs/pr-screenshots/pr-895/01-cancellation-reason-detail.png +0 -0
  298. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
  299. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
  300. package/docs/pr-screenshots/pr-901/projects-tab-default.png +0 -0
  301. package/docs/pr-screenshots/pr-901/projects-tab-fmf-selected.png +0 -0
  302. package/docs/pr-screenshots/pr-916/model-picker-AFTER-crop.png +0 -0
  303. package/docs/pr-screenshots/pr-916/model-picker-AFTER.png +0 -0
  304. package/docs/pr-screenshots/pr-916/model-picker-BEFORE-crop.png +0 -0
  305. package/docs/pr-screenshots/pr-916/model-picker-BEFORE.png +0 -0
  306. package/docs/pr-screenshots/pr-916/model-picker-dropdown-AFTER.png +0 -0
  307. package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
  308. package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
  309. package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
  310. package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
  311. package/docs/visual-evidence-ci.md +0 -103
  312. package/engine/bridge.js +0 -379
  313. package/engine/quarantine-refs.js +0 -103
  314. package/engine/state-operations.js +0 -178
  315. /package/engine/{steering-constraints.js → agents/steering-constraints.js} +0 -0
@@ -46,7 +46,7 @@ The Constellation agent polls:
46
46
  GET http://127.0.0.1:7331/api/constellation/v1/snapshot
47
47
  ```
48
48
 
49
- The endpoint is loopback-only because the Minions dashboard binds `127.0.0.1`. It supports `ETag` and `If-None-Match`; unchanged state returns `304` with no body. A `200` response is a **complete replacement snapshot**, so consumers must remove previously mirrored entities that are absent from the latest response.
49
+ The endpoint is loopback-only because the Minions dashboard binds `127.0.0.1`. It is gated by `bridge.isBridgeEnabled` — it returns `404` with `{"error": "Constellation bridge is disabled", "code": "bridge-disabled"}` unless `engine.constellationBridge.enabled` is literally `true`, so `minions bridge disable` severs the metadata projection at the HTTP layer rather than merely asking the agent to stop polling. When enabled it supports `ETag` and `If-None-Match`; unchanged state returns `304` with no body. A `200` response is a **complete replacement snapshot**, so consumers must remove previously mirrored entities that are absent from the latest response.
50
50
 
51
51
  ```json
52
52
  {
@@ -72,7 +72,7 @@ The endpoint is loopback-only because the Minions dashboard binds `127.0.0.1`. I
72
72
 
73
73
  ### Privacy boundary
74
74
 
75
- Every entity is rebuilt from an explicit allowlist in `engine/bridge.js`; raw dashboard records are never forwarded. The snapshot excludes task descriptions, prompts, acceptance criteria, completion reports, diffs, comments, human feedback, notes, logs, filesystem paths, environment variables, tokens, and secrets. Strings are length-capped to the matching Constellation schema limits.
75
+ Every entity is rebuilt from an explicit allowlist in `engine/api/bridge.js`; raw dashboard records are never forwarded. The snapshot excludes task descriptions, prompts, acceptance criteria, completion reports, diffs, comments, human feedback, notes, logs, filesystem paths, environment variables, tokens, and secrets. Strings are length-capped to the matching Constellation schema limits.
76
76
 
77
77
  The entity shapes match `packages/shared/src/orchestrator-messages.ts` in the Constellation repository:
78
78
 
@@ -84,11 +84,53 @@ The entity shapes match `packages/shared/src/orchestrator-messages.ts` in the Co
84
84
  - `agent_status`: agent/status/project, current task identifiers/title and update time.
85
85
  - `device_summary`: aggregate counts plus protocol, Minions version and engine health.
86
86
 
87
+ ## Capability protocol contract
88
+
89
+ The bridge snapshot above is one-way telemetry. The **capability protocol** is the other half: the vocabulary Minions uses to describe the operations Constellation may invoke. Minions is the producer, Constellation is the consumer, and the vocabulary has exactly one definition:
90
+
91
+ - **Source of truth:** [`engine/api-contracts/capability-protocol.js`](../engine/api-contracts/capability-protocol.js) — plain CommonJS on Node built-ins only (the repo has zero runtime dependencies, so a schema library is not an option). It is the sole definition of the scope vocabulary, the confirmation enum (`none` / `per-call`), the parameter-type set, the capability-entry and manifest-envelope field lists, and the version rule.
92
+ - **Machine-readable artifact:** [`docs/contracts/capability-protocol.v1.json`](contracts/capability-protocol.v1.json), generated with `node engine/api-contracts/capability-protocol.js --emit` and checked in. A unit test fails if the checked-in bytes and the module disagree, and a second test fails if a scope string appears anywhere in this repo outside those two files.
93
+ - **Live negotiation:** `GET http://127.0.0.1:7331/api/capabilities/protocol` serves the same artifact with an `ETag` derived from its checksum. Like every Constellation surface it is gated by `bridge.isBridgeEnabled` — it returns `404` unless `engine.constellationBridge.enabled` is literally `true`.
94
+
95
+ The artifact carries `protocolVersion` and a `sha256:` content `checksum` computed over every other field with object keys sorted, so a consumer can pin the exact contract revision it generated against and detect an edited or swapped copy. Regenerating from unchanged sources reproduces the same bytes — the artifact is content-addressed, not time-stamped.
96
+
97
+ The version rule is the snapshot rule stated above — reused verbatim as `CAPABILITY_VERSION_RULE` rather than restated, so the two cannot drift apart.
98
+
99
+ **Consumers generate, they never transcribe.** Constellation code-generates its enums and types from the artifact and gates on drift; a hand-maintained parallel enum without a drift gate is outside this contract. On the producer side, `validateCapabilityManifest()` **throws** — a manifest declaring a scope, confirmation mode or parameter type outside the contract fails at build time rather than shipping something the consumer cannot generate against.
100
+
101
+ ## Capability manifest (`GET /api/capabilities`)
102
+
103
+ The protocol contract above is the vocabulary; the **capability manifest** is what Minions actually publishes with it. It is built by [`engine/api-contracts/capability-manifest.js`](../engine/api-contracts/capability-manifest.js) from the route-contract catalog and served at `GET http://127.0.0.1:7331/api/capabilities`, behind the same strict `bridge.isBridgeEnabled` gate (`404` unless `engine.constellationBridge.enabled` is literally `true`) and with an `ETag`/`If-None-Match` `304` revalidation path.
104
+
105
+ The envelope is `{ protocol, protocolVersion, generatedAt, minionsVersion, capabilities[] }` and each entry is `{ id, title, description, method, path, scopes[], destructive, confirm, params[] }` — both key sets are generated by walking the protocol contract's field lists, never maintained as a copy alongside them.
106
+
107
+ Two properties define the producer contract:
108
+
109
+ - **Opt-in and fail-closed.** A route reaches the manifest only when its owning module in `engine/api-contracts/` declares an explicit `capability` block on its override. A route that declares nothing is absent — there is no heuristic and no derived-by-default entry. An incomplete block (missing `id`, `title`, `description`, a non-empty `scopes[]`, `destructive` or `confirm`, or an out-of-vocabulary value for any of them) fails the catalog build with a throw.
110
+ - **Authorization is declared, never inferred.** `destructive` and `confirm` come from that block alone. They are deliberately **not** derived from `dashboard.js#_routeDestructive`, which is a fail-open regex over the route path *and its free-text description* — reusing it would let rewording a description silently reclassify a capability's safety posture for a remote caller. The method-derived read-only/state-changing split is used only to **contradict** a claim: a `destructive: false` capability on a state-changing route must supply a `nonDestructiveRationale`, and a `destructive: true` capability must require per-call confirmation.
111
+
112
+ `params[]` is projected from the audited route contract's `request.{path,query,body,headers}.fields` — the same source `GET /api/routes` uses — so a published parameter type cannot drift from the contract the route actually validates against. Contract types that refine a JSON type (`identifier`, `basename`, `path`, `array<object>`) normalize to their protocol type; an ambiguous union (`string|array<string>`) has no honest protocol type and throws, which is the signal to tighten the route contract before publishing it as a capability.
113
+
114
+ ### Phase-0 allowlist
115
+
116
+ The set of annotated routes is deliberately tiny. Phase 0 exists to prove the producer → consumer round trip end to end, so it exposes **four read-only projections and no mutation at all**:
117
+
118
+ | Capability id | Route |
119
+ | ---------------- | ------------------------- |
120
+ | `plan.list` | `GET /api/plans` |
121
+ | `pr.list` | `GET /api/pull-requests` |
122
+ | `status.summary` | `GET /api/status` |
123
+ | `work.list` | `GET /api/work-items` |
124
+
125
+ Every one declares `destructive: false`, `confirm: "none"`, and a single read scope. `status.summary` grants one scope rather than the union of the read scopes: since issue #2949 the status snapshot is only the live engine/throttle/project envelope, and the work, plan, and PR slices it used to carry are separately-declared capabilities of their own.
126
+
127
+ The allowlist is pinned as a snapshot in `test/unit/capability-allowlist.test.js`, which also asserts that every manifest entry uses a read-only HTTP method. Widening it therefore cannot happen quietly — it is a reviewed diff in that table. **Mutating capabilities are not part of this phase**; they arrive with `engine.constellationBridge.commandsEnabled` (default off), which must keep them out of the manifest *and* refuse them at execution while it is false.
128
+
87
129
  ## Marker-file contract
88
130
 
89
131
  The Constellation agent's bridge writes a marker file on every successful poll. Minions reads it with `minions bridge status` so a local operator can verify the bridge is alive and Constellation is talking to it.
90
132
 
91
- - **Path:** `~/.minions/engine/constellation-bridge.json` (exposed as `CONSTELLATION_BRIDGE_MARKER_PATH` in `engine/shared.js`).
133
+ - **Path:** `~/.minions/engine/constellation-bridge.json` (exposed as `CONSTELLATION_BRIDGE_MARKER_PATH` in `engine/core/shared.js`).
92
134
  - **Owner:** the Constellation agent. The Minions engine **never** writes this file — it is a one-way breadcrumb from Constellation → Minions.
93
135
 
94
136
  ### Schema (`schemaVersion: 1`)
@@ -104,13 +146,85 @@ The Constellation agent's bridge writes a marker file on every successful poll.
104
146
 
105
147
  | Field | Required | Notes |
106
148
  | --------------- | -------- | ------------------------------------------------------------------------ |
107
- | `schemaVersion` | yes | Must equal `1`. Any other value causes Minions to ignore the marker entirely (treated as no-marker, same as a missing file). New fields are added behind a deliberate version bump. |
149
+ | `schemaVersion` | yes | Must equal `1`. Any other value causes Minions to ignore the marker entirely (treated as no-marker, same as a missing file). New required fields are added behind a deliberate version bump. |
108
150
  | `lastSeenAt` | yes | ISO-8601 UTC timestamp of the last successful poll. |
109
- | `agentVersion` | no | Constellation agent semver string, surfaced in `bridge status`. |
151
+ | `agentVersion` | no | Constellation agent semver string, surfaced in `bridge status` and used by the companion probe below. |
110
152
  | `source` | no | Free-form identifier, expected `"constellation-agent"` today. |
153
+ | `healthUrl` | no | Loopback URL of the companion's own health endpoint. Optional and additive: readers must tolerate its absence, and Minions refuses any non-loopback value without requesting it. |
111
154
 
112
155
  Writers MUST use an atomic-replace pattern (`write to tmp + rename`) so a partial write never leaves Minions reading a half-baked JSON blob.
113
156
 
157
+ ## Companion probe
158
+
159
+ Minions reports whether a **compatible Constellation companion** is present. It only reports — Minions **never downloads, executes, or installs Constellation**, there is no recursion marker and no reciprocal-bootstrap handshake, and companion absence is a visible degraded state that is never fatal. Minions installs and operates independently.
160
+
161
+ The probe lives in `engine/api/companion.js` and is surfaced by:
162
+
163
+ - `minions doctor` — one `Constellation companion` row (always `warn`, never a critical failure, so the doctor exit code is unchanged on hosts without Constellation).
164
+ - `minions bridge health` — printed before the snapshot probe, so it still says something useful when the dashboard is down.
165
+ - `minions init` and `minions update` — non-fatal informational output.
166
+
167
+ ### Statuses
168
+
169
+ | Status | Meaning |
170
+ | ------------------------- | ------------------------------------------------------------------------------------------- |
171
+ | `companion-ok` | A companion registered and its version is inside the declared compatible range. |
172
+ | `companion-incompatible` | A companion is present but its version is outside the range, or its marker uses a `schemaVersion` this Minions build does not read. |
173
+ | `companion-missing` | No marker — no companion has ever registered with this Minions instance. |
174
+ | `companion-unknown` | A companion is present but its state could not be determined: unreadable marker, no declared version, or a refused/failed/timed-out health probe. |
175
+
176
+ Every report prints the **detected version** and the **declared compatible range**, so an incompatibility is always actionable rather than a bare "not supported".
177
+
178
+ ### How detection works
179
+
180
+ 1. **Marker** — `~/.minions/engine/constellation-bridge.json` (the same file documented above) is the presence signal. There is no PATH lookup and no directory-name heuristic anywhere in the probe.
181
+ 2. **Bounded health request (optional)** — when the marker declares a `healthUrl` (or an operator configures one), Minions issues **one** `GET` under an explicit timeout to resolve a version the marker omitted. An operator-configured `healthUrl` is probed even when no marker exists, so a companion that is installed but not yet bridged is not misreported as absent. The URL must be loopback (`127.0.0.1`, `localhost`, `::1`) over `http`/`https`; anything else is refused **without being requested**, so the probe can never make an outbound network request. A refusal, failure, or timeout reports `companion-unknown` — never `companion-missing`.
182
+
183
+ With no `healthUrl` anywhere — the default — the marker alone decides, and no request of any kind is made.
184
+
185
+ Both stages are bounded, so a hung companion can never stall `minions doctor`, `minions bridge health`, `minions init`, `minions update`, or engine start. The probe uses Node built-ins only; Minions stays zero-dependency at runtime.
186
+
187
+ ### Setup guidance
188
+
189
+ On anything other than `companion-ok`, Minions prints the documented Constellation install command for the current platform as **text for the operator to run**:
190
+
191
+ ```text
192
+ Constellation companion: companion-missing
193
+ version: (unknown) compatible range: >=0.2.0 <1.0.0
194
+ no Constellation companion has registered with this Minions instance
195
+ Minions runs fine without it — Constellation only mirrors state into its HUD.
196
+ To install or update the companion, run it yourself (PowerShell):
197
+ irm https://icy-water-0224cc51e.2.azurestaticapps.net/install.ps1 | iex
198
+ Docs: https://icy-water-0224cc51e.2.azurestaticapps.net/
199
+ Minions never runs it for you — this is guidance, not an action.
200
+ ```
201
+
202
+ On macOS/Linux the printed command is `curl -fsSL https://icy-water-0224cc51e.2.azurestaticapps.net/install.sh | sh`. Minions never executes either command.
203
+
204
+ ### Config + opt-out
205
+
206
+ ```json
207
+ {
208
+ "engine": {
209
+ "constellationCompanion": {
210
+ "probeEnabled": true,
211
+ "probeTimeoutMs": 2000,
212
+ "healthUrl": null
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ | Key | Default | Notes |
219
+ | ---------------- | ------- | -------------------------------------------------------------------------------------- |
220
+ | `probeEnabled` | `true` | Set to `false` to suppress the companion probe **entirely** — no marker read, no health request, no output anywhere. Intended for managed deployments that already know the companion topology. |
221
+ | `probeTimeoutMs` | `2000` | Hard bound on the optional health request. Clamped to `[250, 15000]` ms at read time. |
222
+ | `healthUrl` | `null` | Operator override for the companion health endpoint. Wins over the marker's own `healthUrl`. Loopback-only. |
223
+
224
+ The environment variable **`MINIONS_DISABLE_COMPANION_PROBE=1`** does the same as `probeEnabled: false` and wins over config, so a fleet image can suppress the probe without editing `config.json`.
225
+
226
+ This block is CLI/config-managed like `engine.constellationBridge` — neither Constellation surface has a dashboard Settings control.
227
+
114
228
  ## `minions bridge health`
115
229
 
116
230
  `bridge health` performs a synchronous probe of the versioned snapshot endpoint and prints its aggregate metadata. This verifies both dashboard reachability and protocol compatibility.
@@ -136,8 +250,22 @@ bridge: dashboard reachable on http://127.0.0.1:7331
136
250
 
137
251
  If the dashboard is not listening, `bridge health` prints `dashboard not running on :7331 — start it with \`minions dash\`` and exits 1. Use this exit code to gate scripted health checks.
138
252
 
253
+ Because the snapshot endpoint is gated, probing it with the bridge disabled is not a dashboard fault. `bridge health` recognizes the `bridge-disabled` response and reports the flag instead of a bare status code, still exiting 1:
254
+
255
+ ```text
256
+ error: Constellation bridge is disabled — enable it with `minions bridge enable`
257
+ config: engine.constellationBridge.enabled = false
258
+ ```
259
+
139
260
  ## Cross-repo coordination
140
261
 
141
- The Minions producer lands before the Constellation consumer. During rollout, Constellation may fall back to the legacy per-endpoint projection only when the versioned endpoint returns `404`; a malformed versioned response must fail closed rather than silently downgrading.
262
+ The Minions producer lands before the Constellation consumer. During rollout, Constellation may fall back to the legacy per-endpoint projection only when the versioned endpoint is genuinely absent; a malformed versioned response must fail closed rather than silently downgrading.
263
+
264
+ **A `404` is not by itself proof of absence.** The same status now also means "this operator turned the bridge off", and those two cases require opposite behavior — fall back in one, stop entirely in the other. Discriminate on the response body, not the status code:
265
+
266
+ - `404` with `"code": "bridge-disabled"` → the bridge is deliberately off. **Stop projecting.** Do not fall back to the legacy per-endpoint projection; doing so would keep mirroring the machine the operator just withdrew, which is precisely what `enabled` exists to prevent.
267
+ - Any other `404` (or a non-JSON body) → the route is not served by this Minions build; the legacy fallback applies.
268
+
269
+ In practice an unmatched path on the Minions dashboard falls through to the SPA catch-all and answers `200 text/html`, never `404`, so on any build that has this route a `404` here is always the gate.
142
270
 
143
271
  The Constellation agent's bridge reads `~/.minions/config.json` directly (no Minions HTTP API call) so config edits propagate without waiting for the engine to restart.
@@ -65,10 +65,10 @@ Key design properties:
65
65
  | `metrics_daily` rollups | SQL metrics `_daily.<YYYY-MM-DD>` → `{costUsd, inputTokens, outputTokens, cacheRead, tasks}` |
66
66
  | Per-entity aggregates | Per-agent counters: `tasksCompleted/Errored`, `prsCreated/Approved/Rejected/Merged`, `reviewsDone`, `totalCostUsd`, `totalInputTokens/OutputTokens/CacheRead`, `totalRuntimeMs`, `model`, `timedTasks` |
67
67
  | App/engine analytics | SQL metrics `_engine`: `command-center`, `agent-dispatch`, `consolidation`, `agent_memory_reconcile` |
68
- | Emit helpers | `llm.trackEngineUsage()`, `trackReviewMetric()`, `updateMetrics()` (`engine/lifecycle.js`, `engine/llm.js`, `engine/shared.js`) |
68
+ | Emit helpers | `llm.trackEngineUsage()`, `trackReviewMetric()`, `updateMetrics()` (`engine/orchestration/lifecycle.js`, `engine/agents/llm.js`, `engine/core/shared.js`) |
69
69
  | Structured error taxonomy | `FAILURE_CLASS` constants (recorded per work-item; not yet aggregated as a metric) |
70
70
  | Diagnostic stream | SQL log tail (secret-redacted via `redactSecrets()`) + `engine/dashboard-diagnostics.log` |
71
- | Admin overview query | `getMetrics()` (`engine/queries.js`) surfaced via `/api/status` |
71
+ | Admin overview query | `getMetrics()` (`engine/core/queries.js`) surfaced via `/api/status` |
72
72
 
73
73
  ## The gap (the 40% worth building)
74
74
 
@@ -156,6 +156,6 @@ the other HTML entry points; counters from an in-memory registry, no client libr
156
156
  `packages/server/src/api/prism.ts`, `packages/dashboard/src/prism/prism-telemetry.ts`
157
157
  - Constellation taxonomy/privacy: `packages/shared/src/constants.ts`,
158
158
  `packages/shared/src/privacy.ts`
159
- - Minions today: `engine/state.db` metrics, `getMetrics()` (`engine/queries.js`),
160
- `trackEngineUsage()` (`engine/llm.js`), `FAILURE_CLASS` (`engine/shared.js`),
159
+ - Minions today: `engine/state.db` metrics, `getMetrics()` (`engine/core/queries.js`),
160
+ `trackEngineUsage()` (`engine/agents/llm.js`), `FAILURE_CLASS` (`engine/core/shared.js`),
161
161
  related design doc `design-state-storage.md`.
@@ -0,0 +1,165 @@
1
+ {
2
+ "protocol": "minions.capability.manifest",
3
+ "protocolVersion": 1,
4
+ "generator": "engine/api-contracts/capability-protocol.js",
5
+ "versionRule": "`protocolVersion` changes only for a breaking schema change. Additive optional fields do not require a version bump.",
6
+ "scopes": [
7
+ "work:read",
8
+ "work:write",
9
+ "plan:read",
10
+ "plan:write",
11
+ "pr:read",
12
+ "notes:write",
13
+ "watch:write",
14
+ "engine:control",
15
+ "cc:converse",
16
+ "docchat:write",
17
+ "qa:read"
18
+ ],
19
+ "confirmModes": [
20
+ "none",
21
+ "per-call"
22
+ ],
23
+ "paramTypes": [
24
+ "string",
25
+ "number",
26
+ "integer",
27
+ "boolean",
28
+ "array",
29
+ "object"
30
+ ],
31
+ "manifestEnvelopeFields": [
32
+ {
33
+ "name": "protocol",
34
+ "type": "string",
35
+ "required": true,
36
+ "constant": "minions.capability.manifest",
37
+ "description": "Protocol identifier."
38
+ },
39
+ {
40
+ "name": "protocolVersion",
41
+ "type": "integer",
42
+ "required": true,
43
+ "constant": 1,
44
+ "description": "Protocol version; see versionRule."
45
+ },
46
+ {
47
+ "name": "generatedAt",
48
+ "type": "string",
49
+ "required": true,
50
+ "description": "ISO-8601 UTC timestamp of manifest generation."
51
+ },
52
+ {
53
+ "name": "minionsVersion",
54
+ "type": "string",
55
+ "required": false,
56
+ "description": "Optional Minions package version of the producer."
57
+ },
58
+ {
59
+ "name": "capabilities",
60
+ "type": "array",
61
+ "required": true,
62
+ "itemsOf": "capabilityEntryFields",
63
+ "description": "Capability entries."
64
+ }
65
+ ],
66
+ "capabilityEntryFields": [
67
+ {
68
+ "name": "id",
69
+ "type": "string",
70
+ "required": true,
71
+ "description": "Stable capability identifier, unique within a manifest."
72
+ },
73
+ {
74
+ "name": "title",
75
+ "type": "string",
76
+ "required": true,
77
+ "description": "Short human-readable label."
78
+ },
79
+ {
80
+ "name": "description",
81
+ "type": "string",
82
+ "required": true,
83
+ "description": "One-line description of the effect."
84
+ },
85
+ {
86
+ "name": "method",
87
+ "type": "string",
88
+ "required": true,
89
+ "description": "HTTP method of the backing Minions route."
90
+ },
91
+ {
92
+ "name": "path",
93
+ "type": "string",
94
+ "required": true,
95
+ "description": "Canonical route path template of the backing Minions route."
96
+ },
97
+ {
98
+ "name": "scopes",
99
+ "type": "array",
100
+ "required": true,
101
+ "itemsOf": "scopes",
102
+ "description": "Scopes consumed, drawn from the scope vocabulary."
103
+ },
104
+ {
105
+ "name": "destructive",
106
+ "type": "boolean",
107
+ "required": true,
108
+ "description": "Whether invoking the capability destroys or irreversibly mutates state. A consumer authorization input, so it is always declared explicitly and never inferred from the route name, method, or description."
109
+ },
110
+ {
111
+ "name": "confirm",
112
+ "type": "string",
113
+ "required": true,
114
+ "allowedValuesOf": "confirmModes",
115
+ "description": "Confirmation policy for invocation."
116
+ },
117
+ {
118
+ "name": "params",
119
+ "type": "array",
120
+ "required": true,
121
+ "itemsOf": "capabilityParamFields",
122
+ "description": "Parameter descriptors; may be empty."
123
+ }
124
+ ],
125
+ "capabilityParamFields": [
126
+ {
127
+ "name": "name",
128
+ "type": "string",
129
+ "required": true,
130
+ "description": "Parameter name as sent to the route."
131
+ },
132
+ {
133
+ "name": "type",
134
+ "type": "string",
135
+ "required": true,
136
+ "allowedValuesOf": "paramTypes",
137
+ "description": "Parameter type from the parameter-type set."
138
+ },
139
+ {
140
+ "name": "required",
141
+ "type": "boolean",
142
+ "required": true,
143
+ "description": "Whether the caller must supply the parameter."
144
+ },
145
+ {
146
+ "name": "description",
147
+ "type": "string",
148
+ "required": false,
149
+ "description": "Optional one-line parameter description."
150
+ },
151
+ {
152
+ "name": "allowedValues",
153
+ "type": "array",
154
+ "required": false,
155
+ "description": "Optional closed value set."
156
+ },
157
+ {
158
+ "name": "maxLength",
159
+ "type": "integer",
160
+ "required": false,
161
+ "description": "Optional maximum length for string parameters."
162
+ }
163
+ ],
164
+ "checksum": "sha256:28a2266d5b4f4a4618073461a06b07380121419603ca31f2539eb8263cfe9318"
165
+ }
@@ -1,17 +1,17 @@
1
1
  # `saveCooldowns` merge semantics
2
2
 
3
3
  > **Historical design record:** this predates the SQL-only cutover. Cooldowns now
4
- > persist through `engine/small-state-store.js`; file and lock references below
4
+ > persist through `engine/persistence/small-state-store.js`; file and lock references below
5
5
  > describe the superseded implementation evaluated by the original PRD.
6
6
 
7
7
  Scoping deliverable for PRD item **P-bfa3a-scope-cooldown-merge** (Wave 3, PR10
8
8
  pre-work) of the 2026-05-27 weekly bug-audit plan. Decides the merge strategy
9
9
  the implementation PR (**P-bfa3b-cooldown-lost-update**) will apply at
10
- `engine/cooldown.js:63-101`. No code is modified by this scoping artefact.
10
+ `engine/orchestration/cooldown.js:63-101`. No code is modified by this scoping artefact.
11
11
 
12
12
  ## Context
13
13
 
14
- `engine/cooldown.js:63-101` (`saveCooldowns`) handles only **deletions** across
14
+ `engine/orchestration/cooldown.js:63-101` (`saveCooldowns`) handles only **deletions** across
15
15
  the debounced in-memory ↔ on-disk boundary. Any key added to `cooldowns.json`
16
16
  by a concurrent writer between two of our writes is **silently overwritten**
17
17
  when the in-memory `dispatchCooldowns` Map is flushed via
@@ -20,7 +20,7 @@ the window.
20
20
 
21
21
  `mutateCooldowns` already runs through `mutateJsonFileLocked`, which acquires
22
22
  an exclusive `withFileLock` for the read-modify-write
23
- (source: `engine/shared.js` `mutateCooldowns` ~L1625 and `mutateJsonFileLocked` ~L1542), so the callback
23
+ (source: `engine/core/shared.js` `mutateCooldowns` ~L1625 and `mutateJsonFileLocked` ~L1542), so the callback
24
24
  receives the freshly-read `diskCooldowns` snapshot — but the current code
25
25
  throws that snapshot away.
26
26
 
@@ -53,7 +53,7 @@ acceptance from P-bfa3b verbatim.
53
53
  after the next `loadCooldowns()` on writer B.
54
54
 
55
55
  2. **Deletion-merge preserved (regression).** The existing
56
- `_lastDiskCooldownKeys` logic at `engine/cooldown.js:70-74` runs first,
56
+ `_lastDiskCooldownKeys` logic at `engine/orchestration/cooldown.js:70-74` runs first,
57
57
  before the union — any key present in `_lastDiskCooldownKeys` (i.e. we
58
58
  wrote it on our previous flush) but absent from the freshly-read
59
59
  `diskCooldowns` is treated as a deliberate delete by another writer and is
@@ -73,7 +73,7 @@ acceptance from P-bfa3b verbatim.
73
73
  ## Trade-offs for the three options NOT chosen
74
74
 
75
75
  - **(b) per-key timestamp-tagged merge** — The entry schema already carries
76
- `timestamp` (source: `engine/cooldown.js:154, 171, 195`), so this would add
76
+ `timestamp` (source: `engine/orchestration/cooldown.js:154, 171, 195`), so this would add
77
77
  no new field; but it adds per-key branching in the flush hot path while
78
78
  converging on the same winner as (a) in every realistic case, because the
79
79
  in-memory entry whose mutation triggered the debounce is by construction at
@@ -88,12 +88,12 @@ acceptance from P-bfa3b verbatim.
88
88
  `setCooldown` / `setCooldownFailure` / `isOnCooldown` call adds one fs read
89
89
  to the per-dispatch evaluation hot path, contradicts the module-level
90
90
  `dispatchCooldowns` Map cache that exists specifically to avoid that cost
91
- (source: `engine/cooldown.js:39`), and the lock-protected merge in (a)
91
+ (source: `engine/orchestration/cooldown.js:39`), and the lock-protected merge in (a)
92
92
  achieves the same correctness without the per-read penalty.
93
93
 
94
94
  ## Implementation notes for the P-bfa3b dispatch (dallas)
95
95
 
96
- - Touch only `engine/cooldown.js:63-101` (`saveCooldowns`) — no API or signature
96
+ - Touch only `engine/orchestration/cooldown.js:63-101` (`saveCooldowns`) — no API or signature
97
97
  changes, no caller-side migration. Expected diff: ~10–15 LoC inside the
98
98
  existing `mutateCooldowns` callback.
99
99
  - The expiry-prune loop and the `pendingContexts` cap/truncate loop must run on
@@ -115,10 +115,10 @@ acceptance from P-bfa3b verbatim.
115
115
 
116
116
  ## Source references
117
117
 
118
- - `engine/cooldown.js:63-101` — current lost-update site
119
- - `engine/cooldown.js:38-60` — `loadCooldowns` + `_lastDiskCooldownKeys` baseline
120
- - `engine/shared.js` `mutateCooldowns` (~L1625) — already lock-protected
121
- - `engine/shared.js` `mutateJsonFileLocked` (~L1542) — reads disk inside the
118
+ - `engine/orchestration/cooldown.js:63-101` — current lost-update site
119
+ - `engine/orchestration/cooldown.js:38-60` — `loadCooldowns` + `_lastDiskCooldownKeys` baseline
120
+ - `engine/core/shared.js` `mutateCooldowns` (~L1625) — already lock-protected
121
+ - `engine/core/shared.js` `mutateJsonFileLocked` (~L1542) — reads disk inside the
122
122
  lock; `skipWriteIfUnchanged` enabled for cooldowns
123
123
  - `prd/bug-fix-plan-from-weekly-audit-2026-05-27.json` — P-bfa3a (this scoping)
124
124
  and P-bfa3b (implementation acceptance criteria)
@@ -28,7 +28,7 @@
28
28
  | `capabilities.claudeMdNativeDiscovery` | **`false`** | Copilot loads its native `AGENTS.md`, not repo-authored `CLAUDE.md`; the engine may inject the latter through its separate propagation layer. |
29
29
  | `capabilities.fallbackModel` | **`false`** | No `--fallback-model` flag. |
30
30
  | `capabilities.sessionPersistenceControl` | **`false`** | Copilot manages session state internally in `~/.copilot/session-state/`. Engine cannot opt out without `--config-dir`. |
31
- | `capabilities.streamConsumer` | **`true`** | The adapter supplies the stream accumulator used by `engine/llm.js`. |
31
+ | `capabilities.streamConsumer` | **`true`** | The adapter supplies the stream accumulator used by `engine/agents/llm.js`. |
32
32
  | `capabilities.imageInput` | **`true`** | Base64 image payloads are materialized to temp files and passed as `--attachment <path>` (repeatable flag, W-mqv7324u0021db5d). See §10a. |
33
33
  | `capabilities.acpWorkerPool` | **`true`** | `copilot --acp` supports the capability-gated CC and fleet worker pools. |
34
34
 
@@ -88,7 +88,7 @@ When falling back to the gh-hosted form, the adapter must return:
88
88
  { bin: '<path-to-gh.exe>', native: true, leadingArgs: ['copilot'] }
89
89
  ```
90
90
 
91
- so that `engine/spawn-agent.js` invokes `gh copilot <flags>` rather than
91
+ so that `engine/agents/spawn-agent.js` invokes `gh copilot <flags>` rather than
92
92
  `copilot <flags>`. The availability probe does not guarantee that every
93
93
  built-in/extension version accepts the standalone agent flags. The path remains
94
94
  best-effort; preflight invokes the resolved command with `leadingArgs` and
@@ -130,7 +130,7 @@ copilot -p $big --output-format json -s --allow-all --no-ask-user --autopilot
130
130
  ### Decision
131
131
 
132
132
  Set `capabilities.promptViaArg = false`. The adapter **does not** emit
133
- `--prompt <text>` in args; instead, `engine/spawn-agent.js` pipes the final
133
+ `--prompt <text>` in args; instead, `engine/agents/spawn-agent.js` pipes the final
134
134
  prompt (system block prepended) via stdin. This:
135
135
 
136
136
  - Sidesteps the Windows 32 KB ARG_MAX cliff for any prompt that bundles
@@ -176,7 +176,7 @@ Empirically confirmed flags for non-interactive Copilot invocations:
176
176
  | `--allow-all` | **required** | Equivalent to `--allow-all-tools --allow-all-paths --allow-all-urls`. Without this the CLI prompts for every tool/path use, which deadlocks in stdin/stdout mode. |
177
177
  | `--no-ask-user` | **required** | Removes the `ask_user` tool. Without it the agent can stall waiting for human input. |
178
178
  | `--autopilot` | for multi-turn agency | Enables `task_complete`-driven multi-turn loop. **Without it** the session ends after one assistant response (see §3.1). |
179
- | `--log-level <level>` | **do not emit** | Copilot CLI 1.0.76-1 exits silently with code 1 for explicit non-default levels. Omit the flag and use the CLI default; `--output-format json` still keeps stdout machine-readable. |
179
+ | `--log-level <level>` | **do not emit** | Copilot CLI 1.0.76-1 and 1.0.76-2 exit silently with code 1 for explicit non-default levels. Omit the flag and use the CLI default; `--output-format json` still keeps stdout machine-readable. |
180
180
  | `--disable-builtin-mcps` | gated by config | Disables `github-mcp-server`. Default-on for Minions (`copilotDisableBuiltinMcps: true`) to prevent split-brain PR creation. |
181
181
  | `--no-color` | always emitted | Keeps any incidental CLI rendering free of ANSI color. |
182
182
  | `--plain-diff` | always emitted | Keeps incidental diff rendering plain even though normal output is JSONL. |
@@ -187,7 +187,7 @@ Empirically confirmed flags for non-interactive Copilot invocations:
187
187
  | `--stream on` / `--stream off` | optional | Default is `on`. See §4. |
188
188
  | `--enable-reasoning-summaries` | optional | Maps from `opts.reasoningSummaries`; only Anthropic models populate `assistant.reasoning_delta`. |
189
189
  | `--add-dir <path>` | adapter-supported, normally omitted | Registers extra read-allowed dirs when `opts.addDirs` is supplied. Normal Minions dispatch leaves this empty and relies on Copilot's native cwd/user-config discovery. |
190
- | `--attachment <path>` | injected for image inputs | Passes an image file to the model. Repeatable. The adapter writes each base64 image payload inside the call-scoped temp directory and emits one `--attachment` per file; `engine/llm.js` removes the directory after the process settles. Gated by `capabilities.imageInput`. |
190
+ | `--attachment <path>` | injected for image inputs | Passes an image file to the model. Repeatable. The adapter writes each base64 image payload inside the call-scoped temp directory and emits one `--attachment` per file; `engine/agents/llm.js` removes the directory after the process settles. Gated by `capabilities.imageInput`. |
191
191
  | `-v` / `--verbose` | **never emit** | Does not exist on Copilot. The Claude adapter emits `--verbose`; the Copilot adapter MUST NOT. |
192
192
 
193
193
  ### 3.1 `--autopilot` vs single-shot
@@ -198,7 +198,7 @@ Empirically confirmed flags for non-interactive Copilot invocations:
198
198
  | no `--autopilot` | `assistant.turn_end` → `result` | One-shot Q&A. Fewer events; no `session.task_complete`, no `session.info`. Closer match for CC / doc-chat use cases that don't need multi-turn. |
199
199
 
200
200
  The Minions agent path (engine.js dispatch) uses autopilot. CC and doc-chat in
201
- `engine/llm.js` should also use autopilot — they need tool use even when only
201
+ `engine/agents/llm.js` should also use autopilot — they need tool use even when only
202
202
  one assistant turn is expected — but the parser must tolerate the absence of
203
203
  `session.task_complete` because some early-exit paths skip it.
204
204
 
@@ -652,7 +652,7 @@ When the spike's findings disagree with the plan text, **this document wins**
652
652
  `capabilities.imageInput: true`. The Copilot CLI accepts image files via
653
653
  `--attachment <path>` (the flag is repeatable). The adapter's `buildArgs(opts)`
654
654
  materializes each base64 payload from `opts.images` to a temp file in
655
- `opts.tmpDir` and appends `--attachment <path>` per file. `engine/llm.js`
655
+ `opts.tmpDir` and appends `--attachment <path>` per file. `engine/agents/llm.js`
656
656
  removes that call-scoped directory after the process settles.
657
657
 
658
658
  ```js
@@ -34,7 +34,7 @@ A cross-repo plan is detected by a single signal at parse time: `shared.extractP
34
34
  **Projects:** minions, minions-opg
35
35
  ```
36
36
 
37
- Both forms accept `,`, `|`, or `;` as separators with surrounding whitespace, and strip surrounding quotes. See `engine/shared.js:4516-4559` (`extractPlanDeclaredProject` / `extractPlanTargetProjects`) for the exact regexes.
37
+ Both forms accept `,`, `|`, or `;` as separators with surrounding whitespace, and strip surrounding quotes. See `engine/core/shared.js:4516-4559` (`extractPlanDeclaredProject` / `extractPlanTargetProjects`) for the exact regexes.
38
38
 
39
39
  The dashboard's Create-Plan modal (P-b51c08af) renders a multi-select project picker. Submitting two or more projects normalizes to a deduped array and `POST /api/plans/create` writes the plan with the plural `**Projects:**` header + HTML comment marker, no singular `**Project:**` line. Selecting zero or one project keeps the legacy singular header (no behavioural change for single-project plans).
40
40
 
@@ -136,18 +136,18 @@ This is what makes the per-project dispatcher's later `git worktree add ... orig
136
136
 
137
137
  `engine.js#discoverFromWorkItems(config, project)` reads exactly one project SQL scope per call, routes pending items via `routing.md`, and returns `newWork[]` entries for the dispatcher. Cross-repo plans don't change this loop; the engine iterates all configured projects per tick.
138
138
 
139
- Module-level `_claimedAgents` in `engine/routing.js` persists across in-process calls but is cleared at the start of each discovery pass (`routing.resetClaimedAgents()`), so two items in different projects routed to the same agent in the same pass contend correctly for the single agent slot.
139
+ Module-level `_claimedAgents` in `engine/orchestration/routing.js` persists across in-process calls but is cleared at the start of each discovery pass (`routing.resetClaimedAgents()`), so two items in different projects routed to the same agent in the same pass contend correctly for the single agent slot.
140
140
 
141
141
  ## Per-project verify fan-out
142
142
 
143
- `engine/lifecycle.js#checkPlanCompletion` (`engine/lifecycle.js:26-155`, fan-out at `:279-494`) gates on two conditions:
143
+ `engine/orchestration/lifecycle.js#checkPlanCompletion` (`engine/orchestration/lifecycle.js:26-155`, fan-out at `:279-494`) gates on two conditions:
144
144
 
145
145
  1. Every PRD `missing_features` item has been materialized into a WI.
146
146
  2. Every materialized WI is in `PLAN_TERMINAL_STATUSES` (`DONE_STATUSES`, `failed`, or `cancelled`).
147
147
 
148
148
  When both gates pass, the function groups **active PRs** by project from the SQL PR store and joins them to done items through the `pr_links` table. The result is a `touchedProjects` array: every project that has at least one active PR linked to one of the plan's done items.
149
149
 
150
- For each touched project the fan-out (`engine/lifecycle.js:312-494`):
150
+ For each touched project the fan-out (`engine/orchestration/lifecycle.js:312-494`):
151
151
 
152
152
  - Looks up any existing verify WI keyed on `(sourcePlan, itemType:'verify', project)`. Legacy single-WI plans match against the primary project name when `existingVerify.project` is unset.
153
153
  - Skips if the existing verify is active; re-opens if it's eligible (`isReopenableVerify`).
@@ -165,7 +165,7 @@ The verify agents then run in parallel — one per touched project, each in its
165
165
 
166
166
  Plan card and detail views surface the cross-repo shape:
167
167
 
168
- - **`i.projects[]` per PRD item + plan `_projects` rollup** (P-e8d49105, `engine/queries.js`) — populated when at least one PRD item carries a `project` field. Plan card uses `_projects.length >= 2` as the gate for cross-repo UI variations.
168
+ - **`i.projects[]` per PRD item + plan `_projects` rollup** (P-e8d49105, `engine/core/queries.js`) — populated when at least one PRD item carries a `project` field. Plan card uses `_projects.length >= 2` as the gate for cross-repo UI variations.
169
169
  - **Per-project status rollup pills** (P-66b1faec, `dashboard.js#handlePlansList`) — when `Object.keys(_perProjectProgress).length >= 2`, the plan card meta line renders one pill per project with a per-project completion count (e.g. `minions 3/4 · minions-opg 1/2`). Single-project plans render the legacy aggregate progress bar unchanged.
170
170
  - **Per-project verify badges in plan detail** (P-d7e592b1, `dashboard/js/render-plans.js`) — when `verifyWis.length >= 2`, the detail view renders one labelled verify badge per project; single-project plans render the legacy single badge unchanged.
171
171
 
@@ -214,7 +214,7 @@ Plan markdown `plans/cross-repo-launch.md`:
214
214
  Add a new compliance check + matching engine plumbing.
215
215
  ```
216
216
 
217
- Operator approves the plan. Plan-to-prd dispatches against the `minions` project (read-only worktree, first in the target list). The agent writes `agents/<agent-id>/prd-result.json`; the engine validates the envelope, imports its `prd` object into SQLite under filename `cross-repo-launch.json`, and deletes the sidecar. The imported PRD shape is:
217
+ Operator approves the plan. Plan-to-prd dispatches against the `minions` project (read-only worktree, first in the target list). The agent writes `agents/<agent-id>/prd-result.json` declaring the engine-pinned `filename` and the dispatched `work_item`; the engine validates the envelope, imports its `prd` object into SQLite under filename `cross-repo-launch.json`, and consumes the sidecar. The cross-repo filename (`cross-<uid>-<date>.json`) is generated once and then pinned on the work item, so a retry keeps the same expected identity instead of orphaning the previous attempt's result — see [plan-lifecycle.md](plan-lifecycle.md#plan-to-prd-result-sidecar). The imported PRD shape is:
218
218
 
219
219
  ```json
220
220
  {
@@ -258,7 +258,7 @@ When you need to change the cross-repo path, the load-bearing seams are:
258
258
 
259
259
  | Concern | File:line | Notes |
260
260
  | --- | --- | --- |
261
- | Parse plan markdown projects | `engine/shared.js:4516` (`extractPlanDeclaredProject`), `engine/shared.js:4539` (`extractPlanTargetProjects`) | Both scan first 80 lines; HTML comment marker preferred over `**Projects:**` fallback. |
261
+ | Parse plan markdown projects | `engine/core/shared.js:4516` (`extractPlanDeclaredProject`), `engine/core/shared.js:4539` (`extractPlanTargetProjects`) | Both scan first 80 lines; HTML comment marker preferred over `**Projects:**` fallback. |
262
262
  | `POST /api/plans/create` array normalization | `dashboard.js:13958-14006` | Dedup + per-name validation via `shared.findProjectByName`; 0–1 → singular header, 2+ → plural header + marker. |
263
263
  | Plan-to-prd dispatch wiring | `engine.js:10319-10506` (`discoverCentralWorkItems`) | Sets `vars.target_projects` when plan is cross-repo; picks first listed project for the read-only worktree. |
264
264
  | Plan-to-prd playbook contract | `playbooks/plan-to-prd.md` (P-3b8c40d9) | Tells the agent to omit top-level `project`, set per-item `project`, default to `parallel`. |
@@ -267,11 +267,11 @@ When you need to change the cross-repo path, the load-bearing seams are:
267
267
  | Shared-branch pre-create per project | `engine.js:7718-7756` | Idempotent `rev-parse` → `branch` → `push -u`; per-project failure isolation. |
268
268
  | Cross-repo dep partitioning + advisory fetch | `engine.js:4457-4658` (`spawnAgent` dep loop) | `isCrossRepo` split; transfer-fetch via filesystem-path remote into `refs/cross-repo-deps/<branch>`. |
269
269
  | Cross-repo dep prompt section | `engine.js:793-823` (`buildCrossRepoDepsSection`) | Renders one section per dep with repo, branch, SHA, files, local ref. |
270
- | Per-project verify fan-out | `engine/lifecycle.js:279-494` (`checkPlanCompletion`) | Group active PRs by project via SQL links; one verify WI per touched project. |
271
- | Verify WI creation | `engine/lifecycle.js:350-410`, `engine/lifecycle.js:445-494` | `itemType:'verify'`, `project: projName`, project-prefixed title when `touchedProjects.length > 1`. |
270
+ | Per-project verify fan-out | `engine/orchestration/lifecycle.js:279-494` (`checkPlanCompletion`) | Group active PRs by project via SQL links; one verify WI per touched project. |
271
+ | Verify WI creation | `engine/orchestration/lifecycle.js:350-410`, `engine/orchestration/lifecycle.js:445-494` | `itemType:'verify'`, `project: projName`, project-prefixed title when `touchedProjects.length > 1`. |
272
272
  | Per-project rollup pills | `dashboard.js:7826-7845`, `dashboard/js/render-plans.js:389-417` | Gate `Object.keys(_perProjectProgress).length >= 2`. |
273
273
  | Per-project verify badges | `dashboard/js/render-plans.js:351-417` | Gate `verifyWis.length >= 2`. |
274
- | `i.projects[]` + plan `_projects` rollup | `engine/queries.js:2442-2444`, `dashboard.js:7813-7826` | Populated when at least one PRD item has a `project` field. |
274
+ | `i.projects[]` + plan `_projects` rollup | `engine/core/queries.js:2442-2444`, `dashboard.js:7813-7826` | Populated when at least one PRD item has a `project` field. |
275
275
 
276
276
  ## Executable spec
277
277
 
@@ -9,12 +9,12 @@ This file records false-positive findings from past dead-code audits so subseque
9
9
  "id": "reviewFeedbackSourceMatches-not-dead-exported",
10
10
  "retractedOn": "2026-06-03",
11
11
  "retractedBy": "Dead Code Review meeting (Lambert, Dallas, Ripley unanimous)",
12
- "claim": "engine/lifecycle.js reviewFeedbackSourceMatches is still dead-exported",
13
- "correction": "The function is NOT exported (verify: grep `reviewFeedbackSourceMatches` in engine/lifecycle.js module.exports block at :5161-5220 returns zero hits) and IS live-called intra-file at engine/lifecycle.js:2939 inside createReviewFeedbackForAuthor.",
12
+ "claim": "engine/orchestration/lifecycle.js reviewFeedbackSourceMatches is still dead-exported",
13
+ "correction": "The function is NOT exported (verify: grep `reviewFeedbackSourceMatches` in engine/orchestration/lifecycle.js module.exports block at :5161-5220 returns zero hits) and IS live-called intra-file at engine/orchestration/lifecycle.js:2939 inside createReviewFeedbackForAuthor.",
14
14
  "sources": [
15
- "engine/lifecycle.js:2883 (definition)",
16
- "engine/lifecycle.js:2939 (live caller inside createReviewFeedbackForAuthor)",
17
- "engine/lifecycle.js:5161-5220 (module.exports block — function absent)",
15
+ "engine/orchestration/lifecycle.js:2883 (definition)",
16
+ "engine/orchestration/lifecycle.js:2939 (live caller inside createReviewFeedbackForAuthor)",
17
+ "engine/orchestration/lifecycle.js:5161-5220 (module.exports block — function absent)",
18
18
  "knowledge/project-notes/2026-06-03-ripley-meeting-conclusion-dead-code-review-2026-06-03.md",
19
19
  "notes/inbox/ripley-2026-06-03-1012.md (R2 retraction)"
20
20
  ],