@yemi33/minions 0.1.2448 → 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 +58 -4
  7. package/dashboard/js/detail-panel.js +36 -0
  8. package/dashboard/js/memory-panel.js +59 -12
  9. package/dashboard/js/qa.js +179 -20
  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 +13 -2
  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
@@ -6,10 +6,10 @@ the **quarantine path** (dirty/divergent → quarantine dir + retry), and the
6
6
  **Windows file-lock retry** (EPERM/EBUSY footgun). CLAUDE.md → Worktree
7
7
  Lifecycle keeps the cross-cutting invariants; the detail lives here.
8
8
 
9
- > Source of truth: `engine/worktree-pool.js`, `engine/shared.js#removeWorktree`
9
+ > Source of truth: `engine/worktrees/pool.js`, `engine/core/shared.js#removeWorktree`
10
10
  > + `_retryFsOp`, `engine.js` (`_quarantineDirtyWorktree`,
11
11
  > `_killGitDescendantsForWorktree`, `pruneOrphanWorktrees*`, `gcDispatchWorktreeIfOrphan`),
12
- > `engine/cleanup.js`. Last verified: 2026-07-23.
12
+ > `engine/orchestration/cleanup.js`. Last verified: 2026-07-23.
13
13
 
14
14
  ## Operator-checkout boundary
15
15
 
@@ -76,14 +76,14 @@ dispatch was still active.
76
76
  (`excludeDispatchId: id` so the dispatch can clean up its own worktree);
77
77
  dispatch-end orphan GC (`gcDispatchWorktreeIfOrphan`); `_quarantineDirtyWorktree`
78
78
  (returns `{ skipped: true, quarantinedPath: null }` on skip — callers
79
- MUST honor `skipped` and not set `quarantined: true`); `engine/cleanup.js`
79
+ MUST honor `skipped` and not set `quarantined: true`); `engine/orchestration/cleanup.js`
80
80
  orphan-dir sweep.
81
81
  - **On skip:** drops a deduped operator note at
82
82
  `notes/inbox/engine-worktree-skip-live-<basename>-<date>.md`.
83
83
 
84
84
  ## Worktree pool (opt-in)
85
85
 
86
- `ENGINE_DEFAULTS.worktreePoolSize > 0` enables `engine/worktree-pool.js` to
86
+ `ENGINE_DEFAULTS.worktreePoolSize > 0` enables `engine/worktrees/pool.js` to
87
87
  recycle worktree dirs across branches.
88
88
 
89
89
  - **Borrow** in `spawnAgent` only when (a) the new branch doesn't exist on
@@ -107,6 +107,88 @@ that intentionally preserve an existing branch continue using that branch
107
107
  unchanged. Warm-pool borrowing keeps its existing fetch-and-checkout of
108
108
  `origin/<mainBranch>`.
109
109
 
110
+ ### Shared remote-ref refresh serialization (W-ms5tb6ha016fda10)
111
+
112
+ `refs/remotes/origin/<mainBranch>` is shared by every dispatch in a clone. Two
113
+ fresh-worktree preparations in the same tick both run `git fetch origin
114
+ <mainBranch>`, git takes a `<ref>.lock` for the update, and the loser dies with:
115
+
116
+ ```
117
+ error: cannot lock ref 'refs/remotes/origin/main': is at <sha> but expected <sha>
118
+ ```
119
+
120
+ That is a **local serialization defect**, not connectivity. Two mitigations:
121
+
122
+ 1. **`shared.withSerializedRemoteRefRefresh(repoPath, fn)`** — an in-process,
123
+ per-repository async mutex that every shared-ref fetch runs inside
124
+ (`_fetchWithTransientRetry`, reuse-sync, pool borrow, pool return, dependency
125
+ pre-merge). The engine daemon issues the concurrent fetches, so chaining them
126
+ per repo removes the race at its source. Deliberately **not** a file lock — a
127
+ file lock would be held across a network round-trip, which the concurrency
128
+ rules forbid. Keyed on the resolved repo path, so unrelated projects still
129
+ fetch in parallel, and it wraps **only** the fetch (never the follow-up merge,
130
+ checkout, or worktree add).
131
+ 2. **`FAILURE_CLASS.GIT_REF_LOCK`** — classification for the residual
132
+ cross-process case (an operator's own `git fetch`, the dashboard, the CLI).
133
+ Previously these classified as `NETWORK_ERROR`, which hid the real cause
134
+ behind a "flaky network" story and burned dispatch retries. `GIT_REF_LOCK` is
135
+ **retryable** (the lock clears in seconds) and
136
+ `_fetchWithTransientRetry` now retries once on it before surfacing.
137
+
138
+ `shared.isGitRefLockError(err)` is the predicate; it inspects `message`,
139
+ `stderr`, and `stdout` (exec rejections put the actionable git text on stderr)
140
+ and deliberately does not match genuine network/auth failures.
141
+
142
+ ## Control-plane dispatch: the cheapest worktree is the one never created
143
+
144
+ Some scheduled maintenance work is 100% Minions/REST + `gh`/`az` and never reads
145
+ or writes repository files — "merge approved PRs", "sweep the PR tracker", "CI
146
+ health check". Allocating a checkout for it is pure cost, and it was also
147
+ *harmful*: those dispatches raced the shared-ref fetch above and failed jobs that
148
+ needed no checkout at all.
149
+
150
+ A work item (or schedule) may declare `meta.executionSurface: "control-plane"`.
151
+ `shared.resolveSpawnPaths` then short-circuits **before** live-checkout mode and
152
+ before the read-only / mutating split, returning
153
+ `{ cwd: MINIONS_DIR, worktreeRootDir: null, controlPlane: true }`. No worktree is
154
+ created, no branch is derived, and the branch mutex, live-mode concurrency cap,
155
+ operator branch-hold probe, and worktree free-space preflight are all skipped.
156
+
157
+ The opt-in is **structured and fail-closed** — never inferred from prose:
158
+
159
+ | | |
160
+ |---|---|
161
+ | Field | `meta.executionSurface` (`"repo"` \| `"control-plane"`; unset ⇒ `"repo"`) |
162
+ | Eligible types | `shared.CONTROL_PLANE_ELIGIBLE_TYPES` — `setup`, `ask`, `explore`, `meeting` |
163
+ | Rejection class | `FAILURE_CLASS.INVALID_EXECUTION_SURFACE` (non-retryable) |
164
+ | Validated at | `POST /api/work-items` (create + update final-state guard), `validateScheduleDefinition`, and again in `spawnAgent` |
165
+
166
+ Eligibility is an **allowlist**, not a denylist: a repo-reading/mutating type
167
+ (`fix`, `implement`, `review`, `test`, `verify`, `decompose`, `docs`,
168
+ `build-fix-complex`) can never declare `control-plane`, an unknown surface string
169
+ is rejected rather than defaulted, and an invalid declaration degrades to a
170
+ normal repo dispatch rather than silently skipping the checkout.
171
+
172
+ Project context is preserved without a checkout: the rendered prompt still
173
+ carries `project_name` / `repo_name` / `repo_host` / `main_branch`, plus an
174
+ injected `## Execution surface: control-plane` section
175
+ (`engine/agents/playbook.js#buildControlPlaneHint`) telling the agent the repo is not on
176
+ disk, to work through the API and platform CLIs, not to clone, and to stop and
177
+ report if the task turns out to need repository files.
178
+
179
+ Schedules carry the declaration through `createScheduledWorkItem`:
180
+
181
+ ```json
182
+ {
183
+ "id": "merge-approved-prs",
184
+ "cron": "0 */4 *",
185
+ "title": "Every 4h: merge approved PRs",
186
+ "type": "setup",
187
+ "project": "minions-opg",
188
+ "executionSurface": "control-plane"
189
+ }
190
+ ```
191
+
110
192
  ## Creation integrity gate (#905)
111
193
 
112
194
  `engine.js#runWorktreeAdd` treats every nonzero `git worktree add` result as a
@@ -154,6 +236,11 @@ post-reset re-verify) and the dirty-files prompt-injection site
154
236
  A same-named file nested in a subdir is NOT the marker and stays dirty;
155
237
  the marker filter never masks a genuine user/agent edit beside it.
156
238
 
239
+ Agent playbook health checks use the same boundary: an untracked root-level
240
+ marker is expected orchestration metadata and must be preserved, while every
241
+ staged, modified, deleted, or other untracked entry remains source dirt that
242
+ stops the agent before it edits the worktree.
243
+
157
244
  When the dirt is real and the worktree can't be auto-healed, the engine
158
245
  still quarantines and fails non-retryably, and the quarantine
159
246
  auto-recovery loop re-queues the item once in a fresh worktree (see
@@ -171,11 +258,29 @@ quarantine fallback; transient probe, fetch, auth, and push failures leave clean
171
258
  ahead commits in place for retry. Recoverable
172
259
  `refs/minions/quarantine/*` and `refs/minions/quarantine-wip/*` refs are listed
173
260
  on Dashboard → Engine under **Quarantined Work**.
174
- Periodic cleanup deletes these recovery refs after 10 days. This is intentionally
261
+ Periodic cleanup (`engine/orchestration/cleanup.js`, every 60 ticks) deletes these recovery refs
262
+ after 30 days — no operator schedule or manual `minions cleanup` run is required.
263
+ The window is defined once as `QUARANTINE_REF_RETENTION_DAYS` in
264
+ `engine/core/shared.js` (re-exported by `engine/worktrees/quarantine-refs.js`), and
265
+ `ENGINE_DEFAULTS.worktreeProtectedQuarantineRetentionMs` is derived from it so
266
+ the directory and ref windows cannot drift apart. This is intentionally
175
267
  longer than the 24-hour quarantine-directory retention window: the refs preserve
176
268
  committed HEAD and WIP snapshots after the directory itself is removed, while
177
269
  still preventing recovery refs from growing without bound.
178
270
 
271
+ The sweep fails safe. Age comes only from the canonical `/<epoch-ms>` segment
272
+ encoded in the ref name; a ref whose trailing segment is not a plain positive
273
+ integer is **preserved and logged**, never deleted speculatively. Only the
274
+ `refs/minions/quarantine/*` and `refs/minions/quarantine-wip/*` namespaces are
275
+ touched — branches, tags, and near-miss namespaces are ignored — and every delete
276
+ is SHA-guarded (`git update-ref -d <ref> <sha>`) so a concurrently rewritten ref
277
+ is refused rather than clobbered. Commit and WIP refs expire independently on
278
+ their own timestamps, so a pair created by the same quarantine event expires
279
+ together. Each configured project is swept independently: a missing or
280
+ unavailable repository is reported and skipped without aborting the sweep for the
281
+ remaining projects, and repeated sweeps are idempotent. Cleanup logs the removed,
282
+ retained, malformed, and failed counts per run.
283
+
179
284
  At dispatch close, an error result is inspected before orphan GC. A clean branch
180
285
  that is aligned with origin (or successfully pushed there) remains removable.
181
286
  Dirty files, unpushed commits that cannot be pushed, and indeterminate branch
@@ -363,6 +468,36 @@ After `worktreeStuckThreshold` consecutive failures the path is
363
468
  `removeWorktree`** — you'll bypass the retry, escalation, and metrics
364
469
  layers.
365
470
 
471
+ ### Removal is verified, never assumed (W-ms9q3dql00vxd4a8)
472
+
473
+ Every stage of `removeWorktree` clears the worktree's **contents first** —
474
+ including `.git` and the `.minions-worktree` ownership marker — and only then
475
+ unlinks the top directory. On Windows a handle on the directory itself can make
476
+ that final unlink fail while the stage still reports success (`rd /s /q` in
477
+ particular can exit 0 having deleted everything but the root). The husk left
478
+ behind carries no marker, no `gitdir:` pointer and no registration, so every
479
+ safe GC correctly classifies it as `foreignUnmanaged` and keeps it **forever**.
480
+ That is how an install accumulates a hundred empty `W-*` directories nothing is
481
+ ever allowed to reclaim.
482
+
483
+ So `removeWorktree` verifies the outcome instead of trusting the exit code.
484
+ Each success path funnels through `_verifyWorktreeRemoved`, which:
485
+
486
+ 1. returns success when the path is genuinely gone;
487
+ 2. **finishes the job** with a plain `rmdir` when the leftover is empty — an
488
+ empty directory has nothing left to lose;
489
+ 3. otherwise **re-stamps the ownership marker** (a restore of prior ownership,
490
+ never a claim over a path that never had one) and reports the removal as the
491
+ failure it was, so the caller retries and the ordinary orphan sweep can still
492
+ classify and reclaim the residue at its retention boundary.
493
+
494
+ The corollary for the GC: the **marker is the proof of ownership, not `.git`**.
495
+ Owned residue whose `.git` pointer is missing entirely or dangles at pruned
496
+ admin metadata still expires normally. Residue with *no* marker stays foreign
497
+ regardless of age, emptiness, or how engine-shaped its name looks — an operator
498
+ may have created that directory a moment ago and be about to `git worktree add`
499
+ into it.
500
+
366
501
  ## Periodic prune (W-mq5o6bvy000x7191)
367
502
 
368
503
  `pruneWorktreesPeriodic` runs `reconcileStaleWorktreeDirectories`
@@ -389,9 +524,49 @@ remain protected. Deletion requires either `.minions-worktree` or the narrow
389
524
  legacy proof used for old Windows residue: a `W-*` basename plus a `.git` file
390
525
  pointing inside one of the project's common `.git/worktrees` directories.
391
526
  Ordinary unregistered residue is eligible after 2h; unprotected
392
- `-quarantine-<epoch-ms>` residue after 24h. Protected quarantine directories
393
- remain until manual recovery because they are the only durable copy of work
394
- whose backup ref could not be created.
527
+ `-quarantine-<epoch-ms>` residue after 24h. Protected quarantine directories get
528
+ the longest — but no longer unbounded — window: they expire after
529
+ `engine.worktreeProtectedQuarantineRetentionMs` (default **30 days**), whose
530
+ default is *derived* from `engine/core/shared.js#QUARANTINE_REF_RETENTION_MS` — the
531
+ same constant `engine/worktrees/quarantine-refs.js` prunes recovery refs at — so a
532
+ quarantine DIRECTORY and its recovery REFS expire together instead of one
533
+ outliving the other. Setting the value to `0` restores the previous indefinite
534
+ retention; the operator range is 0–365 days.
535
+
536
+ > **Why the default is derived, not duplicated (W-ms87lj8j018j2572).** The two
537
+ > windows were originally independent literals that both happened to read 10 days.
538
+ > When the ref window moved to 30 days they silently drifted, main went red, and
539
+ > `GET /api/worktree-quarantine-refs` would have listed refs whose directories had
540
+ > already been swept. `ENGINE_DEFAULTS.worktreeProtectedQuarantineRetentionMs` now
541
+ > references the shared constant, and a regression test asserts the structural
542
+ > link rather than mere numeric equality.
543
+
544
+ > **Why the OVERRIDE is resolved through one seam too (W-ms87rbe601a66d13).**
545
+ > Deriving the two *defaults* from one constant closes the drift at build time
546
+ > but not at runtime. `worktreeProtectedQuarantineRetentionMs` is an operator
547
+ > knob, and the recovery-ref sweep used to ignore it entirely — `cleanup.js`
548
+ > called `pruneExpiredQuarantineRefs(projects)` with no retention argument, so it
549
+ > stayed pinned to the built-in 30 days. An operator who shortened retention to,
550
+ > say, 3 days therefore shortened only the DIRECTORY sweep and reintroduced the
551
+ > exact divergence the shared constant removed. Both sweeps now resolve the
552
+ > window through `shared.resolveQuarantineRetentionMs(config)`:
553
+ > `engine/worktrees/gc.js` for directories and
554
+ > `engine/orchestration/cleanup.js` → `pruneExpiredQuarantineRefs(projects,
555
+ > { retentionMs })` for refs. A non-positive value means **indefinite retention
556
+ > on both sides** — `pruneExpiredQuarantineRefs` treats it as "expire nothing",
557
+ > never as "everything is already expired". `GET /api/worktree-inventory`
558
+ > reports `recoverableRefs.retentionMs` at the resolved window, so the API never
559
+ > advertises a 30-day recovery story on an install that shortened it.
560
+
561
+ > **Why this changed (W-ms5tb6ha016fda10).** Protected quarantines — the ones
562
+ > whose dirty WIP could NOT be snapshotted to a durable
563
+ > `refs/minions/quarantine-wip/*` ref — were kept forever by *both*
564
+ > `reconcileStaleWorktreeDirectories` and `pruneOrphanWorktrees`. That is how a
565
+ > busy install accumulates quarantine residue with no expiry (20 such dirs
566
+ > observed on 2026-07-29). Every existing protection still applies first: a live
567
+ > SQL claim, a pool member, a keep_processes / managed_spawn cwd anchor, a
568
+ > detected CWD holder, or a missing ownership marker all keep the directory
569
+ > regardless of age, and `isWorktreePathLive` still fails OPEN.
395
570
 
396
571
  `runCleanup` delegates its configured-root orphan pass to the same reconciler;
397
572
  the former fail-open duplicate scan and generic age/count-cap deletion are
@@ -406,6 +581,110 @@ an expected `skipped` outcome, not a failed removal, and is retried on the next
406
581
  sweep after the dispatch becomes terminal. Aggregate logs report mutually
407
582
  meaningful `removed`, `skipped`, `preserved`, and `failed` counts.
408
583
 
584
+ ### Inventory / diagnostics — `GET /api/worktree-inventory`
585
+
586
+ The sweeps above tell you what they *did*. `engine/worktrees/inventory.js`
587
+ answers the question an operator actually asks when a worktree root has 142
588
+ directories and 6 registrations: **what are all these, and which will the engine
589
+ ever reclaim?** It is strictly read-only — no git mutation, no filesystem writes,
590
+ no removal — and classifies every directory under each project's worktree root
591
+ plus every out-of-root git registration into disjoint buckets:
592
+
593
+ | Bucket | Meaning | Engine-removable? |
594
+ |---|---|---|
595
+ | `active` | a non-terminal dispatch claims the path (`isWorktreePathLive`) | never |
596
+ | `pooled` | warm worktree-pool entry | never (while pooled) |
597
+ | `anchored` | held by a live `keep_processes` / `managed_spawn` cwd | never |
598
+ | `registered` | known to `git worktree list`, not otherwise claimed | only when unlocked + unclaimed |
599
+ | `ownedOrphan` | carries `.minions-worktree`, unregistered, not live | **yes — this is the reclaimable backlog** |
600
+ | `quarantined` (+ `quarantinedProtected`) | `-quarantine-<ts>` recovery artifact | after its retention window |
601
+ | `foreignUnmanaged` | no ownership marker | **never** |
602
+
603
+ The response also carries `recoverableRefs` (`refs/minions/quarantine{,-wip}/*`
604
+ counts + their retention window, so operators can see what is still recoverable)
605
+ and `avoidedAllocations` — how many dispatches ran with
606
+ `executionSurface: "control-plane"` and therefore never allocated a checkout.
607
+ That count is **derived from the dispatch records themselves**, so there is no
608
+ counter to migrate, reset, or drift out of sync.
609
+
610
+ Every probe fails safe in the direction of *not* reporting reclaimable work: an
611
+ `isWorktreePathLive` throw classifies as `active`, an unreadable git registry
612
+ sets `registryReadable: false` and reports owned dirs as `registered` (never
613
+ `ownedOrphan`), and both surface an entry in `errors[]`.
614
+
615
+ Liveness is resolved **once per build** via `shared.listLiveWorktreePaths`
616
+ rather than one dispatch-table scan per directory. That helper is read-only and
617
+ deliberately separate from `isWorktreePathLive`: destructive wipe sites keep the
618
+ per-path guard with its `excludeDispatchId` / `excludeSpawnLeaseId` semantics.
619
+ Its failure contract is identical — `{ ok: false }` means "unknown", and the
620
+ inventory then treats every path as `active`.
621
+
622
+ #### The count contract — `engine.worktreeCount` (W-ms9q3dql00vxd4a8)
623
+
624
+ The buckets roll up into a `lifecycle` split that is the **only** supported
625
+ source for a headline "how many worktrees does Minions have?" number:
626
+
627
+ | Field | Meaning |
628
+ |---|---|
629
+ | `lifecycle.owned` | everything the engine's lifecycle created and is responsible for: `active` + `pooled` + `anchored` + `registered` + `ownedOrphan` + `quarantined` |
630
+ | `lifecycle.foreignUnmanaged` | operator scratch checkouts and unclassifiable residue — reported, never deleted, never folded into the headline |
631
+ | `lifecycle.total` | every directory scanned |
632
+
633
+ `GET /api/status` publishes `engine.worktreeCount` (`lifecycle.owned`) and
634
+ `engine.worktreeForeignCount` (`lifecycle.foreignUnmanaged`) from this same
635
+ classifier, so the dashboard chip and the inventory endpoint can never disagree.
636
+ The previous metric was a raw filesystem walk that counted any directory
637
+ containing a `.git` entry, which reported an operator's hand-made review scratch
638
+ checkout as an active Minions worktree (observed: 18 "worktrees", 3 engine-owned)
639
+ while missing engine residue whose `.git` pointer was already gone.
640
+ **Do not reintroduce a second classifier** — extend `inventory.js` instead.
641
+
642
+ Directories are also de-duplicated **across** projects, so two projects whose
643
+ `worktreeRoot` resolves to the same directory classify it once. The second
644
+ project reports `sharedRoot: true`.
645
+
646
+ #### The scan must not block the dashboard event loop
647
+
648
+ `_scanWorktreeCount` runs inside the dashboard process every
649
+ `worktreeCountCacheTtl` (30 s), and `/api/status` must not block the event loop
650
+ for more than ~100 ms or it starves Command Center's SSE stream. Classification
651
+ is a tight loop over N directories, so **one synchronous probe per directory is
652
+ enough to blow that budget** — and N is dominated by `foreignUnmanaged` residue
653
+ the engine deliberately never reclaims, so it grows without bound.
654
+
655
+ `buildWorktreeInventoryAsync` is therefore the only entry point a server may
656
+ call. It pays **every** cost up front and asynchronously — the
657
+ `git worktree list` shell-out per project, the worktree-root directory
658
+ listings, the per-directory ownership / protected-quarantine marker probes, and
659
+ the symlink resolution of both the candidate paths and the `keep_processes` /
660
+ `managed_spawn` anchor cwds — then hands the results to the same synchronous
661
+ classifier, which does pure in-memory lookups. `buildWorktreeInventory` stays
662
+ synchronous and directly unit-testable; when a pre-read is absent or a single
663
+ probe failed, it transparently falls back to the real synchronous helper, so a
664
+ partial pre-read can only make the scan cheaper, never change a classification.
665
+
666
+ Two footguns this encodes:
667
+
668
+ - **Never call `shared.isPathInsideOrEqual` inside a per-directory loop.** It
669
+ symlink-resolves *both* sides on every call, so anchor matching used to cost
670
+ O(directories × anchors) `realpath` syscalls. Measured on a 150-directory root
671
+ with three live process anchors: 105–120 ms of contiguous synchronous work and
672
+ 130 ms of event-loop stall per refresh. Resolve once with
673
+ `shared.pathForComparisonAsync` and compare via
674
+ `shared.isResolvedPathInsideOrEqual`.
675
+ - **Resolve both sides of a comparison with the same twin.**
676
+ `fs.realpathSync.native` can canonicalize case on a case-insensitive volume
677
+ where the promises API does not, so mixing the sync and async resolvers could
678
+ make an already-resolved path miscompare on macOS.
679
+
680
+ `test/unit/worktree-lifecycle-bounded-gc.test.js` guards this behaviourally: it
681
+ counts real synchronous `fs` calls during a live
682
+ `buildWorktreeInventoryAsync` run and asserts zero, and asserts the async and
683
+ synchronous paths classify an identical tree identically. A source-string
684
+ assertion is not sufficient here — the original guard was silently neutralised
685
+ by re-anchoring it onto a thin delegating wrapper, where a "no sync filesystem
686
+ calls" assertion is vacuously true.
687
+
409
688
  ### Scratch-dir skip and bounded reaper (PR #388)
410
689
 
411
690
  The worktree root (`<localPath>/../worktrees/`) is a **shared namespace**: it
@@ -423,7 +702,7 @@ infra / scratch, never a managed worktree. Engine-created worktree dir names
423
702
  never start with `.`. The predicate `shared.isWorktreeRootInfraEntry(name)`
424
703
  (returns `true` when `name` starts with `.`) is now applied at **both**
425
704
  on-disk enumeration sites — `cleanup.js` worktree scan and
426
- `worktree-gc.js#pruneOrphanWorktrees` — so the sweep is O(1) on scratch size
705
+ `engine/worktrees/gc.js#pruneOrphanWorktrees` — so the sweep is O(1) on scratch size
427
706
  regardless of backlog.
428
707
 
429
708
  **Bounded TTL reaper (`cleanup.js#reapAgentScratch`).** Skipping scratch
@@ -467,7 +746,7 @@ Previously unmarked paths are never marked by this recovery.
467
746
 
468
747
  `engine.worktreeMinFreeSpaceBytes` defaults to 10 GiB (`0` disables) and is
469
748
  editable in Dashboard -> Settings -> Worker Pool & Worktrees. Allocation and a
470
- second spawn-time race check call `engine/worktree-preflight.js` before any
749
+ second spawn-time race check call `engine/worktrees/preflight.js` before any
471
750
  `git worktree add`. The probe uses the nearest existing ancestor when the
472
751
  configured worktree root has not been created yet.
473
752
 
@@ -481,7 +760,7 @@ on every allocation tick.
481
760
 
482
761
  ### Locked-`initializing` missing-dir reclaim (W-mqifblkf00149df5)
483
762
 
484
- A third pruner, `worktree-gc.js#reclaimMissingDirWorktrees`, closes a
763
+ A third pruner, `engine/worktrees/gc.js#reclaimMissingDirWorktrees`, closes a
485
764
  branch-bricking gap the two scanners above are blind to. An interrupted
486
765
  `git worktree add` (engine crash / kill during git's `initializing` lock
487
766
  phase) leaves a `.git/worktrees/<id>/` ADMIN entry behind — often carrying
@@ -552,8 +831,8 @@ Windows-only; three layers, each fail-open:
552
831
  dispatch that owned this worktree path (discovered via
553
832
  `_findTerminalWorktreeOwners`, which reads the dispatch store's non-live
554
833
  sections), look up its recorded root PID from the PID file
555
- (`shared.findDispatchPidFile`) and kill that PID plus its full descendant
556
- tree (`shared.listProcessDescendants` → `shared.killImmediate`). Because it
834
+ (`shared.findDispatchPidFile`) and terminate that PID plus its full descendant
835
+ tree through the registered ownership-aware gateway. Because it
557
836
  only ever kills a tree the engine itself spawned for THIS path, it can never
558
837
  touch a live sibling.
559
838
  - **Layer 2 — CWD-scan reap (fallback for orphans the map / PID files no
@@ -585,7 +864,7 @@ Windows-only; three layers, each fail-open:
585
864
 
586
865
  **Two call sites (W-mqinlicl §6).** `_reapWorktreeHolders` runs both BEFORE the
587
866
  quarantine rename (`_quarantineDirtyWorktree`) AND at **dispatch-end GC**
588
- (`worktree-gc.js#gcDispatchWorktreeIfOrphan`, via the injected `reapHolders`
867
+ (`engine/worktrees/gc.js#gcDispatchWorktreeIfOrphan`, via the injected `reapHolders`
589
868
  hook) right before `shared.removeWorktree`. The dispatch-end call passes the
590
869
  ending dispatch's id as `ownerDispatchId` (terminal-owner discovery misses it —
591
870
  the owner is still `active`) plus `excludeDispatchId`, so a dispatch always
@@ -0,0 +1,46 @@
1
+ # Engine Source Layout
2
+
3
+ `engine/` contains the Minions orchestration implementation and the live
4
+ installation's generated runtime files. Tracked JavaScript belongs to a source
5
+ domain below; generated databases, logs, PID files, diagnostics, completions,
6
+ and QA artifacts are runtime state and must remain ignored.
7
+
8
+ ## Source domains
9
+
10
+ | Directory | Responsibility |
11
+ |---|---|
12
+ | `agents/` | Agent spawning, worker pools, LLM/model integration, playbooks, steering, and harness context |
13
+ | `ado/` | Azure DevOps polling, auth, comments, status, and Git credentials |
14
+ | `api/` | API validation, settings validation, and Constellation bridge handling |
15
+ | `api-contracts/` | Dashboard route request/response contracts |
16
+ | `core/` | Shared constants/utilities, queries, feature resolution, safe expressions, and trust fences |
17
+ | `db/` | SQLite connection, schema, migrations, and DB utilities |
18
+ | `memory/` | Consolidation, KB sweeps, retrieval, review learning, and promotion |
19
+ | `observability/` | Memory diagnostics, engine logs, and metrics stores |
20
+ | `operations/` | CLI commands, status checks, and distribution metadata |
21
+ | `orchestration/` | Dispatch, lifecycle, scheduling, routing, pipelines, meetings, cleanup, and timeouts |
22
+ | `persistence/` | Runtime-state stores, DB events, small-state stores, and state operations |
23
+ | `planning/` | Projects, PRDs, work items, issues, validation, and area resolution |
24
+ | `processes/` | Managed/kept processes, launchers, sweeps, and cross-platform process utilities |
25
+ | `providers/` | GitHub and provider-neutral PR comments, reconciliation, cloning, patching, and fix targeting |
26
+ | `qa/` | QA sessions, runs, runbooks, PRD bridge, and process validation |
27
+ | `qa-runners/` | QA runner registry and built-in Playwright/Maestro adapters |
28
+ | `recovery/` | Restart health verification, in-process supervision, and OS-scheduled watchdog recovery |
29
+ | `runtimes/` | Versioned Claude, Copilot, and Codex runtime adapters |
30
+ | `watches/` | Watch registry, action handlers, and SQL store |
31
+ | `worktrees/` | Worktree creation, live checkout, pool, preflight, quarantine, and GC |
32
+
33
+ No tracked JavaScript files belong directly under `engine/`; source modules
34
+ must live in the domain directories above.
35
+
36
+ ## Rules for physical moves
37
+
38
+ 1. Do not add compatibility wrappers unless an external import contract is
39
+ known. Track any wrapper in `docs/deprecated.json` with a removal gate.
40
+ 2. Update source comments, docs, package contents, direct unit imports, and
41
+ source-inspection assertions in the same change.
42
+ 3. Audit every executable path used by spawn/task-scheduler code on Windows,
43
+ Linux, and macOS.
44
+ 4. Never move generated runtime state as part of a source-layout refactor.
45
+ 5. Validate domain tests, syntax/lint, package contents, and the broader unit
46
+ suite after any future physical move.
@@ -1,9 +1,9 @@
1
1
  /**
2
- * engine/ado-comment.js — Azure DevOps mirror of engine/gh-comment.js.
2
+ * engine/ado/comment.js — Azure DevOps mirror of engine/providers/gh-comment.js.
3
3
  *
4
4
  * Posts a minions PR-comment thread to ADO with the same hidden marker, the
5
5
  * same deterministic brand link as the GitHub path — because both are produced by the ONE neutral
6
- * builder `engine/comment-format.js#buildMinionsCommentBody`. This module owns
6
+ * builder `engine/providers/comment-format.js#buildMinionsCommentBody`. This module owns
7
7
  * only the ADO transport (REST `…/threads` POST) and ADO-specific input
8
8
  * validation (orgBase / project / repositoryId / prNumber).
9
9
  *
@@ -15,7 +15,7 @@
15
15
  * brand trailer hyperlinked.
16
16
  *
17
17
  * Auth: a bearer token from `az account get-access-token` (via
18
- * engine/ado-token.js#acquireAdoToken), threaded as `Authorization: Bearer`.
18
+ * engine/ado/token.js#acquireAdoToken), threaded as `Authorization: Bearer`.
19
19
  * Both the token acquisition and the HTTP transport are injectable so the unit
20
20
  * tests run without network or `az`.
21
21
  *
@@ -33,8 +33,8 @@
33
33
  * changes) stay `active` so they block/notify the author.
34
34
  */
35
35
 
36
- const { buildMinionsCommentBody } = require('./comment-format');
37
- const { acquireAdoToken } = require('./ado-token');
36
+ const { buildMinionsCommentBody } = require('../providers/comment-format');
37
+ const { acquireAdoToken } = require('./token');
38
38
 
39
39
  const ADO_API_VERSION = '7.1';
40
40
 
@@ -83,7 +83,7 @@ function buildThreadsUrl({ orgBase, project, repositoryId, prNumber }) {
83
83
  }
84
84
 
85
85
  /**
86
- * Default token acquisition — thin wrapper over engine/ado-token.js so callers
86
+ * Default token acquisition — thin wrapper over engine/ado/token.js so callers
87
87
  * can inject a stub in tests. Returns the bearer token string or throws.
88
88
  */
89
89
  async function _defaultAcquireToken() {
@@ -94,7 +94,7 @@ async function _defaultAcquireToken() {
94
94
  /**
95
95
  * Post a minions PR comment to an Azure DevOps pull request.
96
96
  *
97
- * Mirrors engine/gh-comment.js#postPrComment: builds the marked +
97
+ * Mirrors engine/providers/gh-comment.js#postPrComment: builds the marked +
98
98
  * brand-linked body via the shared builder, then POSTs a new active thread.
99
99
  *
100
100
  * @param {object} args
@@ -181,7 +181,7 @@ module.exports = {
181
181
  ADO_THREAD_STATUS_ACTIVE,
182
182
  ADO_THREAD_STATUS_CLOSED,
183
183
  // Re-export the neutral builder so ADO callers have a single import surface,
184
- // mirroring engine/gh-comment.js.
184
+ // mirroring engine/providers/gh-comment.js.
185
185
  buildMinionsCommentBody,
186
186
  // Internal validators exported for tests.
187
187
  _validateOrgBase,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * engine/ado-git-auth.js — W-mpcuc8i80003a7b3
2
+ * engine/ado/git-auth.js — W-mpcuc8i80003a7b3
3
3
  *
4
4
  * Inject a per-invocation Authorization: Bearer <token> http.extraHeader into
5
5
  * git ops that touch an ADO origin. Solves the headless-dispatch failure mode
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * fatal: could not read Username for 'https://office.visualstudio.com'
10
10
  *
11
- * The token is sourced via the existing `engine/ado-token.js#acquireAdoTokenSync`
11
+ * The token is sourced via `engine/ado/token.js#acquireAdoTokenSync`
12
12
  * helper (az CLI first, azureauth fallback). We cache it for 30 minutes and
13
13
  * back off acquisition retries for 10 minutes on failure so we don't hammer
14
14
  * az / azureauth on every git op during an outage.
@@ -39,8 +39,8 @@
39
39
  * Migrate to env-form if argv exposure becomes a real concern.
40
40
  */
41
41
 
42
- const shared = require('./shared');
43
- const adoToken = require('./ado-token');
42
+ const shared = require('../core/shared');
43
+ const adoToken = require('./token');
44
44
  const { log } = shared;
45
45
 
46
46
  const TOKEN_TTL_MS = 30 * 60 * 1000; // 30 min — matches gh-token cadence