@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
@@ -17,7 +17,25 @@ registry such as `ProjectFeed-ISS@Release`.
17
17
 
18
18
  No manually created PAT is required, accepted, or stored.
19
19
 
20
- ## One command
20
+ ## Entry points
21
+
22
+ Without a checkout, download the launcher for your platform from the
23
+ [Minions page](https://icy-water-0224cc51e.2.azurestaticapps.net/minions) and run
24
+ it from your download folder:
25
+
26
+ ```powershell
27
+ # Windows — https://icy-water-0224cc51e.2.azurestaticapps.net/minions/install-minions.ps1
28
+ az login
29
+ powershell -ExecutionPolicy Bypass -File .\install-minions.ps1
30
+ ```
31
+
32
+ ```bash
33
+ # Linux / macOS — https://icy-water-0224cc51e.2.azurestaticapps.net/minions/install-minions.sh
34
+ az login
35
+ bash ./install-minions.sh
36
+ ```
37
+
38
+ From a repository checkout, the in-repo wrappers are equivalent:
21
39
 
22
40
  ```powershell
23
41
  # Windows (PowerShell)
@@ -34,8 +52,16 @@ scripts\install-internal-minions.ps1
34
52
  node bin/install-internal-minions.js
35
53
  ```
36
54
 
37
- All three entry points are thin wrappers over `bin/install-internal-minions.js`,
38
- so behavior cannot drift between platforms.
55
+ **Every one of these routes ends in `bin/install-internal-minions.js`**, which is
56
+ why they cannot drift apart. The in-repo wrappers are thin shims over it; the
57
+ downloadable launchers stage the verified package into a throwaway folder and
58
+ hand the migration to the copy that ships inside it. The rest of this document
59
+ describes that installer, so it describes all of them.
60
+
61
+ The downloadable launchers take no options of their own — each one always stages
62
+ the newest published version. Every [option below](#options), including
63
+ `--version` to pin an exact build, belongs to `bin/install-internal-minions.js`,
64
+ so reach it through the `node` route to pass one.
39
65
 
40
66
  ## Prerequisites
41
67
 
@@ -53,14 +79,66 @@ so behavior cannot drift between platforms.
53
79
  | `--registry <url>` | the ISS `ProjectFeed-ISS` feed | Override the Azure Artifacts npm registry. Must be `https`. |
54
80
  | `--package <name>` | `@opg-microsoft/minions` | Override the internal package name. Must be scoped. |
55
81
  | `--backup-dir <dir>` | `<runtime root>/backups/internal-install-<timestamp>` | Where the pre-migration backup is written. |
82
+ | `--runtime-root <dir>` | resolved (see [Runtime root](#runtime-root)) | Pin the runtime root (`MINIONS_HOME`) this run reads, backs up, synchronizes, and restarts. Required to disambiguate when more than one candidate root carries runtime state. It is also the flag `minions update` prints when it refuses a cross-channel public install; quote the path if it contains spaces. |
56
83
  | `--keep-public` | off | Leave an existing `@yemi33/minions` global install in place instead of replacing it. |
57
84
  | `--no-restart` | off | Sync runtime files but skip the health-verified restart. |
58
85
  | `--force` | off | Proceed even while agents are active (see [Active agents](#active-agents)). |
59
86
  | `--dry-run` | off | Print the resolved plan and the exact commands; change nothing. |
60
87
  | `--help` | — | Usage. |
61
88
 
62
- Exit codes: `0` success, `1` failure, `2` usage error, `3` refused because agents
63
- are active.
89
+ Exit codes: `0` success, `1` failure, `2` usage error, `3` refused (active agents,
90
+ an ambiguous runtime root, or a `minions` shim the installer cannot prove it owns).
91
+
92
+ ## Runtime root
93
+
94
+ The runtime root is the `MINIONS_HOME` directory this run backs up, synchronizes,
95
+ and restarts. It is resolved **once**, before a token is acquired or npm is
96
+ touched, and then pinned as an explicit `MINIONS_HOME` onto every child process
97
+ the installer spawns (`backup-state`, `minions init --force`, `minions restart`)
98
+ so no step can re-resolve to a different root. An inherited `MINIONS_TEST_DIR` is
99
+ stripped from that environment, because it outranks `MINIONS_HOME` in
100
+ `engine/core/shared.js#resolveMinionsHome` and would silently un-pin every child. The
101
+ pinned root and how it was chosen are printed at the top of the run and in the
102
+ `--dry-run` plan.
103
+
104
+ `resolveAuthoritativeRuntimeRoot()` enumerates every candidate the engine itself
105
+ could pick, deduplicates them by resolved path (highest-priority source wins),
106
+ and scores each by whether it carries a **non-empty** `engine/state.db` (a
107
+ zero-byte database is an aborted init, not a runtime worth protecting):
108
+
109
+ | Candidate | Source |
110
+ |---|---|
111
+ | `--runtime-root <dir>` | explicit operator pin — always wins |
112
+ | `MINIONS_HOME` | explicit environment pin |
113
+ | `~/.minions-root` | the root pointer `minions init` last recorded, which `engine/core/shared.js#resolveMinionsHome` consults |
114
+ | `~/.minions` | the default |
115
+ | the cwd / repo checkout | the nearest ancestor of the working directory containing both `.git` and `engine.js` — the same shape `resolveMinionsHome`'s `preferSourceCheckout` branch would return |
116
+
117
+ A candidate that carries state outranks one that does not, so **a repo checkout
118
+ never wins over a root that already has state**. When two or more candidates
119
+ carry a non-empty `engine/state.db` and no `--runtime-root` was given, the
120
+ installer **refuses** (exit `3`) and names every candidate plus the exact
121
+ `--runtime-root <path>` command to disambiguate — nothing is downloaded,
122
+ uninstalled, or modified. The installer never guesses which runtime is
123
+ authoritative.
124
+
125
+ ## Install-channel marker
126
+
127
+ `minions init` records the runtime's version in `.minions-version` and its source
128
+ commit in `.minions-commit`. Neither says **which npm distribution** produced the
129
+ root, so an internal `@opg-microsoft/minions` root and a public
130
+ `@yemi33/minions` root are otherwise indistinguishable on disk. `init` therefore
131
+ also writes `.minions-package` beside them:
132
+
133
+ ```json
134
+ { "name": "@opg-microsoft/minions", "version": "1.0.76", "installedAt": "2026-07-29T00:00:00.000Z" }
135
+ ```
136
+
137
+ Read it with `shared.readRuntimeChannel(runtimeRoot)`, which returns
138
+ `{ name, version }` or `null`. A **missing** marker means *unknown*, never an
139
+ error: every root initialized before the marker existed has none, and a legacy
140
+ root must keep working. A marker that is corrupt, or that names a package this
141
+ repo would refuse to put on an npm command line, is unknown for the same reason.
64
142
 
65
143
  ## What it does, in order
66
144
 
@@ -69,7 +147,7 @@ The ordering is the safety contract, not a formality. `planSteps()` in
69
147
  by `test/unit/install-internal-minions.test.js`.
70
148
 
71
149
  1. **`acquire-token`** — `az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798`
72
- (the same ADO resource id `engine/ado-token.js` uses).
150
+ (the same ADO resource id `engine/ado/token.js` uses).
73
151
  2. **`validate-feed`** — `npm view <package>@<spec> version` against the feed, and
74
152
  resolve `latest` to a concrete version. **A feed that is unreachable, or a
75
153
  version that does not exist, fails here — before anything is removed.**
@@ -78,26 +156,95 @@ by `test/unit/install-internal-minions.test.js`.
78
156
  safe:** the artifact is on local disk before the public package is removed, so
79
157
  a feed, network, or auth failure during the global install is recoverable
80
158
  offline. Skipped only when no install will run (see step 6).
81
- 4. **`backup-state`** — checkpoints and copies the SQLite state
82
- (`engine/state-operations.js#backupState`: `PRAGMA wal_checkpoint(TRUNCATE)` +
83
- `VACUUM INTO`), then copies `config.json`, `routing.md`, `pinned.md`, parks a
159
+ 4. **`quiesce-services`** — stops any engine/dashboard running against the
160
+ **pinned** runtime root before anything is backed up, uninstalled, or
161
+ installed. Running services are detected from the pid files the runtime
162
+ already writes plus `engine/control.json`; they are stopped through the
163
+ installed CLI (see *Which stop verb is issued* below), and the run then polls
164
+ until the database handles are actually released. Release requires **both**
165
+ signals: the processes are dead *and* `engine/state.db-shm` is absent or
166
+ empty — a live shared-memory index means a connection still holds the WAL. If
167
+ the handles are not released within the bounded timeout the run **fails
168
+ closed**, before any uninstall or package mutation, and prints which stop path
169
+ was taken, the exact argv it issued, and the exact PIDs still holding the
170
+ database. Whether services were actually stopped is recorded so a rollback can
171
+ restart them. Skipped on a machine with no existing runtime.
172
+
173
+ When the run does fail closed here, end the holders it names yourself — the
174
+ refusal prints each surviving process by name and PID, and the dashboard is
175
+ the usual one — then re-run the installer.
176
+
177
+ ### Which stop verb is issued
178
+
179
+ The installer prefers `minions stop --all --wait`, and falls back to the bare
180
+ `minions stop` for a CLI that does not have the whole-stack verb.
181
+
182
+ A bare `minions stop` is **engine-only** by contract: it is delegated to
183
+ `engine.js stop`, which writes stop intent and returns, so it asks only the
184
+ engine to stand down. The dashboard and the supervisor hold `engine/state.db`
185
+ independently, so on a healthy stack the `-shm` gate above could never go
186
+ green after a "successful" stop and every migration was refused with the
187
+ engine, dashboard, and supervisor PIDs all still alive. `stop --all --wait`
188
+ tears the whole stack down in order and blocks until the handles are
189
+ released — which is why this step polls for the release instead of trusting
190
+ the stop.
191
+
192
+ During a public → internal migration the resolved CLI is frequently the
193
+ **older public package**, which predates that verb, so support is detected by
194
+ running `minions help` against the resolved CLI and reading whether its own
195
+ usage advertises `stop --all` and `--wait`. The probe is **behavioral, not a
196
+ version comparison** — the internal feed and the public registry version
197
+ independently, so there is no version ordering to compare across them, and the
198
+ cost of a wrong guess is an unknown flag passed to a live teardown command.
199
+ `help` is used rather than `stop --help` because `stop` is in the CLI's engine
200
+ delegation set: an older CLI would forward `stop --help` to `engine.js stop`
201
+ and actually stop the engine as a side effect of being asked a question.
202
+
203
+ An unreadable, empty, or unrecognized probe answer **falls back** to the
204
+ legacy `minions stop`. Either way the run then polls for the `-shm` release
205
+ itself — the CLI's own verdict is never trusted — so the fail-closed gate is
206
+ identical on both paths. There is no `--force` skip for it, and a stale
207
+ non-empty `-shm` is never accepted.
208
+ 5. **`backup-state`** — writes a consistent standalone copy of the SQLite state
209
+ (`engine/persistence/state-operations.js#backupState`: a best-effort
210
+ `PRAGMA wal_checkpoint(PASSIVE)` followed by an authoritative `VACUUM INTO`),
211
+ then copies `config.json`, `routing.md`, `pinned.md`, parks a
84
212
  copy of the downloaded tarball beside the backup, and writes a `manifest.json`
85
- describing the from/to packages, the artifact, and the preserved paths.
213
+ describing the from/to packages, the artifact, the preserved paths, the
214
+ pinned runtime root **and how it was resolved**, whether services were
215
+ stopped, the pre-migration `summarizeState()` census, and the list of
216
+ retained artifacts.
86
217
  Skipped on a machine with no existing runtime.
87
- The backup runs against the **runtime's own** `engine/state-operations.js`,
218
+ The checkpoint is deliberately `PASSIVE` and non-fatal: an exclusive
219
+ `TRUNCATE`/`RESTART` checkpoint blocks on any other reader, so with the engine
220
+ or dashboard connected it stalls on the busy timeout or raises `SQLITE_BUSY`
221
+ and would abort the backup. `VACUUM INTO` reads a snapshot that already
222
+ includes committed WAL frames, so it is the authoritative step and the only
223
+ one allowed to fail the backup. `backupState` reports
224
+ `{ ok, path, bytes, checkpointed, walBytesAtStart }` so callers can see
225
+ whether quiescence was actually achieved. The `VACUUM INTO` snapshot is the
226
+ authoritative backup — a bare byte-copy of a live `state.db` alone loses
227
+ committed-but-uncheckpointed transactions. Because the database is quiesced
228
+ by the previous step, the run **also** retains the ORIGINAL
229
+ `engine/state.db`, `engine/state.db-wal`, and `engine/state.db-shm`
230
+ byte-for-byte under `<backup-dir>/original/`, beside the snapshot. Those
231
+ bytes are what the state-restoring rollback puts back, and **the retained
232
+ originals — and everything else in the backup directory — are never deleted
233
+ by this installer, under any outcome.**
234
+ The backup runs against the **runtime's own** `engine/persistence/state-operations.js`,
88
235
  which `minions init` copies into the runtime root and which is therefore always
89
236
  present and version-matched to that runtime's `engine/state.db`. It does not
90
237
  depend on a globally installed CLI: `minions init` deliberately excludes `bin/`
91
238
  from the runtime-root copy, and the published public package predates the
92
239
  `minions state` command. An installed CLI's `minions state backup` is kept only
93
240
  as a fallback for runtime roots that predate the SQL store layout.
94
- 5. **`uninstall-public`** — removes an existing global `@yemi33/minions` so the two
241
+ 6. **`uninstall-public`** — removes an existing global `@yemi33/minions` so the two
95
242
  packages never race for the same `minions` bin shim. **Gated:** it runs only
96
243
  once `assessCutoverReadiness()` confirms the internal artifact is downloaded
97
244
  (or already installed at the target version) *and* a state backup exists
98
245
  whenever there was a runtime to back up. Skipped with `--keep-public`, or when
99
246
  no public install is present.
100
- 6. **`repair-shims`** — clears **orphaned** `minions` shims under `npm prefix -g`.
247
+ 7. **`repair-shims`** — clears **orphaned** `minions` shims under `npm prefix -g`.
101
248
  `npm uninstall -g` has just removed the shims it still owned, so anything left
102
249
  under the prefix that dispatches into `@yemi33/minions` or
103
250
  `@opg-microsoft/minions` with no usable package behind it is a leftover from a
@@ -115,7 +262,7 @@ by `test/unit/install-internal-minions.test.js`.
115
262
  directory entry as absent while npm still aborts on it. Each deletion is
116
263
  re-probed afterwards, so a delete that quietly no-ops is reported as a
117
264
  failure with its path rather than as a cleared shim.
118
- 7. **`install-internal`** — `npm install -g <package>@<resolved-version>` against the
265
+ 8. **`install-internal`** — `npm install -g <package>@<resolved-version>` against the
119
266
  feed. **If that fails, the script installs the already-downloaded tarball**
120
267
  (`npm install -g <backup-dir>/<package>.tgz` — no registry, no token, so it
121
268
  still works when the feed is exactly what failed) rather than leaving the
@@ -125,9 +272,9 @@ by `test/unit/install-internal-minions.test.js`.
125
272
  `uninstall-public` is planned. `npm uninstall -g` takes the shim with the
126
273
  package that owns it, so skipping the install alongside an uninstall would
127
274
  remove the command with nothing to restore it.
128
- 8. **`verify-install`** — re-reads the installed `package.json` from disk and fails
275
+ 9. **`verify-install`** — re-reads the installed `package.json` from disk and fails
129
276
  loudly if npm did not actually land the resolved version.
130
- 9. **`verify-shim`** — resolves the global npm prefix and proves the `minions`
277
+ 10. **`verify-shim`** — resolves the global npm prefix and proves the `minions`
131
278
  command itself dispatches into the internal package (`minions.cmd` /
132
279
  `minions.ps1` / the sh shim on Windows, the `<prefix>/bin/minions` symlink
133
280
  elsewhere). Owning the package directory is not the same as owning the
@@ -136,14 +283,52 @@ by `test/unit/install-internal-minions.test.js`.
136
283
  `@yemi33/minions` is a hard failure with the repair commands printed, and so
137
284
  is a shim whose link target no longer exists — naming the right package is
138
285
  not the same as being able to run it.
139
- 10. **`sync-init`** — `minions init --force --skip-start`, the supported runtime
286
+ 11. **`sync-init`** — `minions init --force --skip-start`, the supported runtime
140
287
  synchronization. It overwrites `.js`/`.html` runtime files and adds files the
141
288
  new version requires. `--force` would otherwise also rewrite the shipped
142
289
  `routing.md` and `knowledge/agents/*.md` with package defaults, so the
143
290
  installer snapshots those before the sync and puts them back after it.
144
- 11. **`restart`** — `minions restart`, which is health-verified
145
- (`engine/restart-health.js`: PID + HTTP probe). Skipped with `--no-restart`.
146
- 12. **`cleanup`** — always runs, on success and on every failure path. The
291
+ 12. **`verify-continuity`** — the last gate before `cleanup`, and it runs while
292
+ the services are still quiesced, **before** `restart`. The preserved-path
293
+ check is presence-only, so an **emptied** `engine/state.db` passes it; this
294
+ step instead re-runs the runtime's own `summarizeState()` against the pinned
295
+ root, out-of-process, and feeds the pre- and post-migration censuses to its
296
+ `compareStateSummaries`. Running it before the restart is deliberate: a
297
+ census taken against a live engine races it, and `getDb()` applies pending
298
+ migrations on open (`engine/db/index.js`), so a quiesced pre-restart census
299
+ still exercises the migrations deterministically. It also bounds the blast
300
+ radius of the failure action — the state-restoring rollback overwrites
301
+ `engine/state.db{,-wal,-shm}` with the retained pre-migration bytes, which
302
+ after a restart would destroy every write the restarted engine and dashboard
303
+ had already made, including the migrations that had just succeeded.
304
+
305
+ The gate is an explicit **allowlist** of durable tables
306
+ (`CONTINUITY_DURABLE_TABLES`: work items, pull requests, plans, PRDs and
307
+ their items/verify PRs, memory records, inbox entries, QA runs and
308
+ sessions). Everything else is passed to `compareStateSummaries` as
309
+ `ignoreTables`, because most of the SQL state legitimately shrinks across a
310
+ cutover: `cc_sessions`/`doc_sessions` are invalidated when the
311
+ `prompts/cc-system.md` hash changes — which `sync-init` ships by definition —
312
+ `pending_rebases` is rewritten wholesale every tick, `worktree_pool`,
313
+ `managed_processes`, `schedule_runs`, `metrics` and friends are runtime
314
+ bookkeeping, `pr_mirror_hashes` is dropped outright by migration 018, and
315
+ `logs`/`events`/`dispatches`/`cooldowns` are capped, consumed, or expire. A
316
+ denylist of volatile tables fails open on every table nobody remembered to
317
+ enumerate; an allowlist fails closed. A durable table may declare
318
+ `repairedByMigration`, the schema version of a shipped repair migration that
319
+ deletes rows from it by design (PRD tables and migration 024) — the gate is
320
+ lifted for that table only on a run that actually crosses that version.
321
+
322
+ Any row-count regression, missing table, or non-empty → empty transition in a
323
+ gated table is a **hard failure** that triggers the state-restoring rollback
324
+ below. A census that cannot be *taken* is warned about loudly but is not
325
+ treated as proof of loss, so a healthy runtime is never rolled backward on
326
+ missing evidence.
327
+ 13. **`restart`** — `minions restart`, which is health-verified
328
+ (`engine/recovery/restart-health.js`: PID + HTTP probe). Skipped with `--no-restart`.
329
+ It is the final action of the run, so the machine is only brought back up on
330
+ state `verify-continuity` already proved survived.
331
+ 14. **`cleanup`** — always runs, on success and on every failure path. The
147
332
  temporary npm config always goes; the downloaded artifact is retained (and its
148
333
  path printed) when the run ended with the machine still needing it.
149
334
 
@@ -155,8 +340,35 @@ cannot be honoured by the install and ignored by the probe.
155
340
 
156
341
  After `sync-init` the script re-checks every runtime path that existed
157
342
  beforehand (`config.json`, `engine/state.db`, `notes/`, `notes.md`, `plans/`,
158
- `knowledge/`, `projects/`, `pinned.md`). A path that existed before and is
159
- missing after is a hard failure pointing at the backup, not a silent data loss.
343
+ `knowledge/`, `projects/`, `pinned.md`, `pipelines/`, `prompts/`, `playbooks/`,
344
+ `agents/`). A path that existed before and is missing after is a hard failure
345
+ pointing at the backup, not a silent data loss. That check is presence-only by
346
+ construction, so `verify-continuity` backs it with a row-level census; the
347
+ artifacts that live purely in SQL (schedules, pipelines runs, watches, meetings,
348
+ QA state) are covered by the census rather than by the path list.
349
+
350
+ ### State-restoring rollback
351
+
352
+ `rollbackPackageIfNeeded()` is package-scoped and deliberately never touches
353
+ state. When `verify-continuity` proves state was **lost**, a separate
354
+ state-restoring rollback runs — it is only ever reached once the retained
355
+ original bytes are known to be strictly better than what is on disk. It:
356
+
357
+ 1. quiesces services again — through the same probed stop path and the same
358
+ `-shm` gate as `quiesce-services`, aborting without restoring anything if the
359
+ database is still held (so the backup stays intact and nothing is written into
360
+ a live database), and reporting the stop path and holding PIDs the same way,
361
+ 2. restores `engine/state.db`, `engine/state.db-wal`, and `engine/state.db-shm`
362
+ from `<backup-dir>/original/`,
363
+ 3. restores `config.json`, `routing.md`, and `pinned.md` from the backup, and
364
+ restores the reseedable snapshot through the **same** `restoreReseededFiles`
365
+ copier the normal `sync-init` repair uses,
366
+ 4. reinstalls the previous package, restarts, and **re-verifies** the census
367
+ against the pre-migration one,
368
+ 5. reports precisely what was restored, removed, or failed.
369
+
370
+ Every step re-copies the same retained bytes, so a re-run converges rather than
371
+ compounding: the rollback is idempotent.
160
372
 
161
373
  ## What this is not
162
374
 
@@ -173,7 +385,7 @@ and a re-run:
173
385
 
174
386
  | State | Preserved how |
175
387
  |---|---|
176
- | `engine/state.db` — work items, dispatches, PR records, schedules, pipelines, watches, meetings, PRDs, QA state, small state | Never replaced. Checkpointed to the backup directory (`PRAGMA wal_checkpoint(TRUNCATE)` + `VACUUM INTO`) before the package changes; the **live** database stays in place and the new version runs its own idempotent startup migrations against it. |
388
+ | `engine/state.db` — work items, dispatches, PR records, schedules, pipelines, watches, meetings, PRDs, QA state, small state | Never replaced. Services are quiesced first, then the state is checkpointed into the backup directory (`PRAGMA wal_checkpoint(PASSIVE)` + `VACUUM INTO`) and the original `state.db`/`-wal`/`-shm` bytes are retained under `<backup-dir>/original/` before the package changes; the **live** database stays in place and the new version runs its own idempotent startup migrations against it. A row-level census taken before and after proves nothing was lost, and restores from the retained originals if it was. |
177
389
  | `config.json` | `minions init` never overwrites it (`neverOverwrite` in `bin/minions.js`), and it is copied into the backup directory as well. |
178
390
  | `routing.md`, `knowledge/agents/*.md` | Shipped by the package, so `init --force` *would* rewrite them. Snapshotted before `sync-init` and restored byte-for-byte after it. |
179
391
  | `notes.md`, `notes/`, `pinned.md`, `plans/`, `prd/`, `projects/`, `agents/` | Not shipped by the package, so the sync never touches them. Existence is re-verified after the sync. |
@@ -206,7 +418,7 @@ it does **not** cover rewriting the files underneath a live dispatch.
206
418
 
207
419
  So the script counts live agent PID files under `<runtime root>/engine/tmp/`
208
420
  (both the per-dispatch directory layout and the legacy flat layout, matching
209
- `engine/shared.js#forEachPidFile`) and **defers with exit code 3** when any are
421
+ `engine/core/shared.js#forEachPidFile`) and **defers with exit code 3** when any are
210
422
  alive. Stale PID files from a crashed run do not block anything — each PID is
211
423
  liveness-checked.
212
424
 
@@ -236,31 +448,118 @@ installed: it resolves the active package name from the installed manifest and
236
448
  reinstalls *that* package, so an internal install upgrades itself instead of
237
449
  pulling in the public one.
238
450
 
239
- It does **not** configure a registry. `minions update` shells out to plain
240
- `npm view <pkg> version` / `npm install -g <pkg>@<version>`
241
- (`engine/shared.js#buildNpmViewVersionCommand` / `#buildNpmGlobalInstallCommand`),
242
- so on the internal channel it only reaches `ProjectFeed-ISS` when your **own**
243
- npm configuration already has a persistent scoped registry entry and credentials
244
- for the feed — for example `@opg-microsoft:registry=<base feed URL>` in your user
245
- `.npmrc`. This script's npm config is deliberately temporary and is deleted after
246
- every run, so it leaves nothing behind for `minions update` to reuse.
247
-
248
- Re-running this script is therefore the upgrade path that always works: it
249
- acquires its own short-lived token per run and is idempotent. It is also the
250
- **channel bootstrap** — what moves a machine onto the internal ProjectFeed-ISS
251
- channel (including replacing a public `@yemi33/minions` install) and what installs
252
- the internal package on a machine that has no Minions at all.
451
+ ### The runtime root is the channel authority
452
+
453
+ The manifest at `PKG_ROOT` only answers *which package an install would pull* —
454
+ it is **not** proof of which channel the runtime is on. It resolves the public
455
+ `@yemi33/minions` both from an opg repo checkout (whose own `package.json`
456
+ carries that name) and from a leftover public global shim standing in front of an
457
+ internal runtime. Either way an unguarded update would drop a stale public build
458
+ on top of a newer internal runtime.
459
+
460
+ So the **runtime root** decides. `minions init` records the installing
461
+ distribution in `<runtime root>/.minions-package`
462
+ (`{ name, version, installedAt }`), and `minions update` gates on it via
463
+ `shared.resolveUpdateChannel({ pkgRoot, runtimeRoot })`
464
+ (`engine/core/shared.js`):
465
+
466
+ | Runtime marker | Update would install | Behavior |
467
+ |---|---|---|
468
+ | `@opg-microsoft/minions` | `@opg-microsoft/minions` | Proceeds (in-channel upgrade) |
469
+ | `@yemi33/minions` | `@yemi33/minions` | Proceeds (in-channel upgrade) |
470
+ | internal (any non-public scope) | `@yemi33/minions` | **REFUSED before any npm call**, exit 1 |
471
+ | `@yemi33/minions` | internal | Proceeds, warns (legitimate cutover) |
472
+ | *(no marker — legacy root)* | anything | Proceeds, warns naming both channels |
473
+
474
+ The refusal never attempts a public downgrade and never reaches the registry. It
475
+ prints the supported remediation — this installer, with the runtime root pinned:
476
+
477
+ ```
478
+ scripts\install-internal-minions.ps1 --runtime-root "<runtime root>" --package @opg-microsoft/minions
479
+ ./scripts/install-internal-minions.sh --runtime-root "<runtime root>" --package @opg-microsoft/minions
480
+ node bin/install-internal-minions.js --runtime-root "<runtime root>" --package @opg-microsoft/minions
481
+ ```
482
+
483
+ There is deliberately **no override flag** for a cross-channel public install: a
484
+ public build is a different distribution of the same `minions` bin, not an older
485
+ version of the internal one. Re-run this installer instead.
486
+
487
+ ### Downgrade guard
488
+
489
+ Within a single channel, `minions update` also refuses a target version that is
490
+ semver-lower than the installed one, because `npm view` can resolve *backwards*
491
+ off a stale local packument — the install then succeeds and the post-install
492
+ version-advance check still passes, while the runtime silently moves to an older
493
+ build. The comparison is numeric (`1.0.9` is not newer than `1.0.76`).
494
+
495
+ ```
496
+ minions update # refuses a lower target version
497
+ minions update --allow-downgrade # installs it anyway
498
+ ```
499
+
500
+ The refusal points at `npm cache clean --force`, which is the usual fix.
501
+
502
+ ### Registry configuration
503
+
504
+ On the **internal channel**, `minions update` resolves and installs from this
505
+ feed itself — it does **not** rely on your own npm configuration, and it leaves
506
+ nothing persistent behind. When the installed package is served by a private
507
+ feed (`bin/install-internal-minions.js#resolveInternalFeed`; today
508
+ `@opg-microsoft/minions` → the ISS `ProjectFeed-ISS` base registry), the update:
509
+
510
+ 1. acquires a **short-lived** Azure DevOps token through your existing Azure CLI
511
+ login (`az account get-access-token`) — no PAT, never in argv, logs, or errors;
512
+ 2. stages it in a **0600 temporary npm userconfig** in the OS temp dir, which is
513
+ deleted on success and on every failure path (including a non-zero exit);
514
+ 3. runs `npm view` / `npm install -g` against the feed with the scoped registry
515
+ **pinned on the command line** (`--@opg-microsoft:registry=<feed>`).
516
+
517
+ Step 3 is what makes the route deterministic. npm resolves a *scoped* package
518
+ through `@scope:registry`, and `--registry` only sets the *default* registry — so
519
+ an ambient `@opg-microsoft:registry=<public proxy>` line silently redirected the
520
+ update and npm answered `E404` for a package that exists in the feed. npm's
521
+ precedence is `cli > env > ./.npmrc > --userconfig > global`, so a `.npmrc` in
522
+ your current directory also outranks the temporary userconfig; only the
523
+ command-line pin is authoritative. Feed failures now report the feed that was
524
+ queried plus the two real causes (Azure CLI sign-in, or Azure Artifacts read
525
+ permission / a version that is not published) instead of a public-registry 404.
526
+
527
+ `--registry <url>` overrides the feed for one run and is accepted only on an
528
+ internal channel. The **public** channel is untouched: `@yemi33/minions` still
529
+ updates through plain `npm view` / `npm install -g`
530
+ (`engine/core/shared.js#buildNpmViewVersionCommand` / `#buildNpmGlobalInstallCommand`)
531
+ against whatever registry your npm config points at.
532
+
533
+ ```
534
+ minions update # internal: feed + Azure CLI auth, public: plain npm
535
+ minions update --registry <url> # internal only: override the feed for this run
536
+ ```
537
+
538
+ Re-running this script remains the **channel bootstrap** — what moves a machine
539
+ onto the internal ProjectFeed-ISS channel (including replacing a public
540
+ `@yemi33/minions` install), what installs the internal package on a machine that
541
+ has no Minions at all, and what repairs a half-finished cutover. It is
542
+ idempotent, so it always works as a recovery path; routine in-channel upgrades no
543
+ longer need it.
253
544
 
254
545
  ## Troubleshooting
255
546
 
256
547
  | Symptom | Cause | Fix |
257
548
  |---|---|---|
549
+ | `REFUSED: this would install the public package over an internal runtime` | `minions update` resolved `@yemi33/minions` (repo checkout, or a leftover public global shim) while `<runtime root>/.minions-package` says the runtime is internal | Nothing was installed and the registry was never contacted. Re-run this installer with the printed `--runtime-root "<path>" --package <internal pkg>`. |
550
+ | `REFUSED: … would downgrade the installed …` | `npm view` resolved an older version off a stale local packument | `npm cache clean --force`, then `minions update`. To install the older version deliberately: `minions update --allow-downgrade`. |
551
+ | `Runtime channel is unknown (no .minions-package marker)` | Legacy runtime root that predates the channel marker | Warning only — the update proceeds as before. Run `minions init` to record the channel. |
258
552
  | `could not acquire an Azure DevOps token` | Not signed in, or no subscription selected | `az login`, then `az account set --subscription <id>` |
553
+ | `Could not acquire an Azure DevOps token for the internal feed` (from `minions update`) | Same cause, raised by the in-place updater before any npm call | `az login`, then re-run `minions update`. Nothing was installed and no credential was written. |
554
+ | `Could not read <pkg>@latest from the internal feed` (from `minions update`) | No Azure Artifacts read access to `ProjectFeed-ISS`, or that version is not published to the feed | Request read on `ProjectFeed-ISS`. This replaces the old misleading public-registry `E404`: the update pins the feed on the command line, so it is never the ambient registry answering. |
555
+ | `--registry applies only to an internal channel install` | `minions update --registry <url>` on a public install | Drop the flag; the public channel updates through your own npm registry. |
259
556
  | `could not read <pkg>@<spec> from the feed` | No feed read permission, or the version does not exist | Request Azure Artifacts read on `ProjectFeed-ISS`; check `--version`. Nothing was uninstalled. |
260
557
  | `REFUSED: N agent process(es) are still running` | Active dispatches | Wait for idle, or re-run with `--force` |
558
+ | `REFUSED: the runtime did not release engine/state.db` | A daemon still holds the WAL — usually the dashboard, which `minions stop` leaves running by design | Nothing was uninstalled or modified. Run `minions stop`, then end whatever the error still names — it prints every surviving holder by name and PID — and re-run. |
559
+ | `REFUSED: N candidate runtime roots carry a non-empty engine/state.db` | More than one directory the engine could boot from holds real state — typically `~/.minions` plus a Minions source checkout you launched the installer from | Nothing was downloaded, uninstalled, or modified. The message names every candidate and prints the exact `--runtime-root <path>` command for each; re-run pinned to the one you mean. See [Runtime root](#runtime-root). |
261
560
  | `expected <pkg>@<v> on disk, found …` | Stale npm metadata cache | `npm cache clean --force`, then re-run |
262
561
  | `refusing to uninstall @yemi33/minions — …` | The artifact or the backup is missing | Nothing was removed. Re-run; the message names which precondition failed. |
263
- | `no way to back up the existing runtime state was found` | The runtime root has neither `engine/state-operations.js` nor a resolvable CLI | Nothing was uninstalled. The error lists every path that was probed; run `minions init --force` against the existing install to restore its engine files, then re-run. |
562
+ | `no way to back up the existing runtime state was found` | The runtime root has neither `engine/persistence/state-operations.js` nor a resolvable CLI | Nothing was uninstalled. The error lists every path that was probed; run `minions init --force` against the existing install to restore its engine files, then re-run. |
264
563
  | `the minions shim … still points at @yemi33/minions` | A previous half-finished cutover left the public shim in place | `npm uninstall -g @yemi33/minions`, then re-run. The install commands are printed with the error. |
265
564
  | `npm ERR! EEXIST … <prefix>\minions` on `npm install -g` | **Orphaned shim** — a prior uninstall or interrupted migration removed the package directory but left the `minions` command behind, so nothing reported the old package as installed and npm refused to overwrite the leftover. | Handled automatically by `repair-shims`: just re-run the installer. Details below. |
266
565
  | `REFUSED: … is named minions but was not generated by npm for …` | A file called `minions` under `npm prefix -g` could not be attributed to `@yemi33/minions` or `@opg-microsoft/minions` | The installer never deletes a file it cannot prove it owns. Inspect the named path, move or rename it (or remove it yourself if it is a leftover), then re-run. Nothing was modified. |
@@ -6,7 +6,7 @@ distinct events sharing a naming pattern, before any fix is proposed.
6
6
 
7
7
  ## Background
8
8
 
9
- `engine/consolidation.js:1156-1290` content-hashes and dedups
9
+ `engine/memory/consolidation.js:1156-1290` content-hashes and dedups
10
10
  engine-authored SYSTEM ALERTS (via `shared.isEngineSystemAlert` +
11
11
  `alertHash` frontmatter, see `_alertHashExists`) at **write time** —
12
12
  recurring identical alerts never get a `-2`/`-3` full-body copy. Agent-authored
@@ -16,7 +16,7 @@ so any note that gets classified into `knowledge/<category>/` twice under the
16
16
  same computed filename (`${date}-${agent}-${titleSlug}.md`) always produces a
17
17
  full-body `-2`/`-3` copy at consolidation time.
18
18
 
19
- Separately, `engine/kb-sweep.js` Pass 1 (`_hashDedup`, `kb-sweep.js:34-37,
19
+ Separately, `engine/memory/kb-sweep.js` Pass 1 (`_hashDedup`, `kb-sweep.js:34-37,
20
20
  155-176`) content-hashes **every** KB entry regardless of category and
21
21
  archives byte-identical duplicates to `knowledge/_swept/`, keeping the most
22
22
  recent. This pass is category-agnostic and *would* catch true agent-authored
@@ -98,7 +98,7 @@ last-completed-sweep timestamp).
98
98
  inbox note that hashes/formats similarly — or, per the `-2`/`-3`
99
99
  evidence above, the identical inbox note re-processed), each pass
100
100
  writes a fresh, full-body `-N` copy via `shared.uniquePath`.
101
- 2. `engine/kb-sweep.js` Pass 1 (`_hashDedup`) *does* eventually catch and
101
+ 2. `engine/memory/kb-sweep.js` Pass 1 (`_hashDedup`) *does* eventually catch and
102
102
  archive these byte-identical copies (category-agnostic, keeps most
103
103
  recent) — but only on its own schedule (`AUTO_SWEEP_INTERVAL_MS = 4h`
104
104
  default, `kb-sweep.js:26`) or on manual trigger, not at consolidation
@@ -128,9 +128,9 @@ similarity, since many genuinely distinct events share a title stem.
128
128
 
129
129
  ## Sources
130
130
 
131
- - `engine/consolidation.js:1156-1300` (`isEngineSystemAlert`/`alertHash`
131
+ - `engine/memory/consolidation.js:1156-1300` (`isEngineSystemAlert`/`alertHash`
132
132
  dedup, `classifyToKnowledgeBase`)
133
- - `engine/kb-sweep.js:20-37,155-176` (`_hashEntry`, `_hashDedup`, sweep
133
+ - `engine/memory/kb-sweep.js:20-37,155-176` (`_hashEntry`, `_hashDedup`, sweep
134
134
  interval constant)
135
135
  - `engine/kb-sweep-state.json` (`completedAtIso: 2026-07-07T21:49:00.186Z`)
136
136
  - `knowledge/reviews/2026-07-07-ripley-review-github-opg-microsoft-minions-740-fix-contam{,-2,-3}.md`
@@ -8,7 +8,7 @@
8
8
 
9
9
  - `source_plan` is a plan Markdown basename.
10
10
  - Plan archive responses retain their documented compatibility fields.
11
- - PRD archive and cascade updates use `engine/prd-store.js` transactions.
11
+ - PRD archive and cascade updates use `engine/planning/prd-store.js` transactions.
12
12
  - Work-item cancellation uses scoped SQL work-item mutations.
13
13
 
14
14
  Use the current `handlePlansArchive` implementation and its behavioral tests as
@@ -1,15 +1,15 @@
1
1
  # KB PR #696 – Merge Conflict Resolution in KB-Sweep Documentation
2
2
 
3
- PR #696 resolved a merge conflict in `docs/kb-sweep.md:97-103` where both branch and main branches cited stale line numbers to `engine/kb-sweep.js`. Upstream code shifts had invalidated both references. Resolution consolidated the "Pass 1, 1.5, and 2" structure and verified the correct current location of `NORMALIZE_CONCURRENCY = 5` at `engine/kb-sweep.js:30`.
3
+ PR #696 resolved a merge conflict in `docs/kb-sweep.md:97-103` where both branch and main branches cited stale line numbers to `engine/memory/kb-sweep.js`. Upstream code shifts had invalidated both references. Resolution consolidated the "Pass 1, 1.5, and 2" structure and verified the correct current location of `NORMALIZE_CONCURRENCY = 5` at `engine/memory/kb-sweep.js:30`.
4
4
 
5
5
  ## Key Findings
6
6
 
7
7
  - Merge conflict in `docs/kb-sweep.md:97-103` ("Pass 3 — Per-Entry Rewrite" section) with stale line citations
8
- - Branch cited `engine/kb-sweep.js:L26`, main cited `L30` with outdated "Pass 1 and 2" wording
8
+ - Branch cited `engine/memory/kb-sweep.js:L26`, main cited `L30` with outdated "Pass 1 and 2" wording
9
9
  - Root cause: upstream commits shifted line numbers; neither reference remained valid post-merge
10
- - Resolved by merging branch's "Pass 1, 1.5, and 2" structure with main's corrected line reference to `engine/kb-sweep.js:30`
11
- - Verified `NORMALIZE_CONCURRENCY = 5` present at `engine/kb-sweep.js:30` in merged state
12
- - Syntax validation passed for `engine/shared.js`, `engine/kb-sweep.js`, `engine.js`, `dashboard.js`
10
+ - Resolved by merging branch's "Pass 1, 1.5, and 2" structure with main's corrected line reference to `engine/memory/kb-sweep.js:30`
11
+ - Verified `NORMALIZE_CONCURRENCY = 5` present at `engine/memory/kb-sweep.js:30` in merged state
12
+ - Syntax validation passed for `engine/core/shared.js`, `engine/memory/kb-sweep.js`, `engine.js`, `dashboard.js`
13
13
 
14
14
  ## Action Items
15
15
 
@@ -20,4 +20,4 @@ PR #696 resolved a merge conflict in `docs/kb-sweep.md:97-103` where both branch
20
20
  - PR #696: https://github.com/opg-microsoft/minions/pull/696
21
21
  - Merge commit: b91cb386
22
22
  - Fix comment: https://github.com/opg-microsoft/minions/pull/696#issuecomment-4905945906
23
- - Correct citation: `engine/kb-sweep.js:30` (`NORMALIZE_CONCURRENCY = 5`)
23
+ - Correct citation: `engine/memory/kb-sweep.js:30` (`NORMALIZE_CONCURRENCY = 5`)