@yemi33/minions 0.1.2424 → 0.1.2425

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 (182) hide show
  1. package/bin/install-internal-minions.js +1209 -0
  2. package/bin/minions.js +75 -9
  3. package/dashboard/docs/typography.md +27 -12
  4. package/dashboard/js/command-center.js +13 -11
  5. package/dashboard/js/live-stream.js +1 -1
  6. package/dashboard/js/memory-search.js +388 -43
  7. package/dashboard/js/modal-qa.js +5 -5
  8. package/dashboard/js/qa.js +39 -26
  9. package/dashboard/js/refresh.js +37 -18
  10. package/dashboard/js/render-agents.js +14 -18
  11. package/dashboard/js/render-dispatch.js +14 -74
  12. package/dashboard/js/render-inbox.js +3 -3
  13. package/dashboard/js/render-meetings.js +8 -8
  14. package/dashboard/js/render-other.js +45 -20
  15. package/dashboard/js/render-pipelines.js +6 -6
  16. package/dashboard/js/render-plans.js +14 -14
  17. package/dashboard/js/render-prd.js +46 -46
  18. package/dashboard/js/render-prs.js +9 -65
  19. package/dashboard/js/render-schedules.js +5 -5
  20. package/dashboard/js/render-skills.js +6 -6
  21. package/dashboard/js/render-utils.js +4 -4
  22. package/dashboard/js/render-watches.js +4 -16
  23. package/dashboard/js/render-work-items.js +95 -107
  24. package/dashboard/js/settings.js +175 -74
  25. package/dashboard/layout.html +3 -2
  26. package/dashboard/pages/inbox.html +1 -1
  27. package/dashboard/pages/work.html +1 -1
  28. package/dashboard/shared/model-display.js +9 -0
  29. package/dashboard/shared/watches-source.js +43 -0
  30. package/dashboard/shared/welcome-popup.js +259 -0
  31. package/dashboard/slim/body.html +6 -6
  32. package/dashboard/slim/js/chat.js +4 -2
  33. package/dashboard/slim/js/history.js +10 -3
  34. package/dashboard/slim/js/modals-tiles.js +85 -16
  35. package/dashboard/slim/js/status.js +12 -16
  36. package/dashboard/slim/styles.css +24 -4
  37. package/dashboard/styles.css +62 -27
  38. package/dashboard-build.js +8 -2
  39. package/dashboard.js +3854 -2514
  40. package/docs/README.md +6 -4
  41. package/docs/api-errors.md +144 -0
  42. package/docs/auto-discovery.md +83 -61
  43. package/docs/capture-demos.js +173 -30
  44. package/docs/command-center.md +5 -1
  45. package/docs/completion-reports.md +127 -11
  46. package/docs/constants.md +15 -3
  47. package/docs/constellation-style-telemetry.md +2 -2
  48. package/docs/copilot-cli-schema.md +32 -22
  49. package/docs/cross-repo-plans.md +30 -30
  50. package/docs/demo/memory-system.html +1 -1
  51. package/docs/deprecated.json +132 -18
  52. package/docs/diagnostics-memory.md +8 -4
  53. package/docs/human-vs-automated.md +2 -2
  54. package/docs/internal-install.md +190 -0
  55. package/docs/kb-pr3223-cascade-archiving.md +16 -0
  56. package/docs/kb-pr696-merge-conflict-docs.md +23 -0
  57. package/docs/kb-sweep.md +2 -2
  58. package/docs/keep-processes.md +8 -1
  59. package/docs/live-checkout-mode.md +1 -1
  60. package/docs/managed-spawn.md +21 -3
  61. package/docs/named-agents.md +3 -2
  62. package/docs/plan-lifecycle.md +32 -32
  63. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
  64. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
  65. package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
  66. package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
  67. package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
  68. package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
  69. package/docs/qa-runbook-lifecycle.md +67 -51
  70. package/docs/qa-runbooks.md +2 -2
  71. package/docs/runtime-adapters.md +101 -50
  72. package/docs/security.md +28 -0
  73. package/docs/self-improvement.md +47 -13
  74. package/docs/skills.md +29 -0
  75. package/docs/slim-ux/concepts.md +2 -2
  76. package/docs/specs/agent-configurability.md +43 -41
  77. package/docs/specs/agent-rename.md +13 -13
  78. package/docs/team-memory.md +283 -14
  79. package/docs/tutorials/03-plan-driven-feature.md +3 -3
  80. package/docs/tutorials/04-runtimes-and-harness.md +18 -21
  81. package/docs/tutorials/08-operations-and-recovery.md +3 -2
  82. package/docs/tutorials/README.md +4 -3
  83. package/docs/visual-evidence-ci.md +103 -0
  84. package/docs/watches.md +35 -30
  85. package/docs/worktree-lifecycle.md +151 -13
  86. package/engine/acp-transport.js +1 -0
  87. package/engine/ado.js +80 -33
  88. package/engine/agent-api-validation.js +571 -0
  89. package/engine/agent-worker-pool.js +7 -6
  90. package/engine/api-contracts/agent-content.js +761 -0
  91. package/engine/api-contracts/cc-ops.js +219 -0
  92. package/engine/api-contracts/config-runtime.js +419 -0
  93. package/engine/api-contracts/core.js +138 -0
  94. package/engine/api-contracts/index.js +506 -0
  95. package/engine/api-contracts/orchestration.js +602 -0
  96. package/engine/api-contracts/pull-requests.js +323 -0
  97. package/engine/api-contracts/qa-process.js +764 -0
  98. package/engine/api-contracts/work-plan-prd.js +494 -0
  99. package/engine/api-validation.js +543 -0
  100. package/engine/cc-api-validation.js +586 -0
  101. package/engine/cc-worker-pool.js +9 -1
  102. package/engine/cleanup.js +393 -271
  103. package/engine/cli.js +65 -7
  104. package/engine/comment-classifier.js +70 -5
  105. package/engine/consolidation.js +123 -5
  106. package/engine/content-api-validation.js +606 -0
  107. package/engine/create-pr-worktree.js +22 -1
  108. package/engine/db/migrations/027-review-learning-lifecycle.js +162 -0
  109. package/engine/dispatch.js +33 -0
  110. package/engine/distribution.js +189 -0
  111. package/engine/execution-model.js +59 -0
  112. package/engine/features.js +12 -0
  113. package/engine/github.js +67 -25
  114. package/engine/harness-context.js +313 -0
  115. package/engine/inbox-store.js +1 -1
  116. package/engine/kb-sweep.js +161 -38
  117. package/engine/keep-process-sweep.js +43 -3
  118. package/engine/lifecycle.js +1066 -165
  119. package/engine/llm.js +4 -0
  120. package/engine/managed-spawn.js +463 -48
  121. package/engine/meeting.js +197 -34
  122. package/engine/memory-retrieval.js +280 -0
  123. package/engine/memory-store.js +821 -38
  124. package/engine/model-discovery.js +17 -3
  125. package/engine/pipeline.js +802 -14
  126. package/engine/plan-prd-validation.js +745 -0
  127. package/engine/playbook.js +206 -17
  128. package/engine/pooled-agent-process.js +6 -1
  129. package/engine/pr-action.js +1 -1
  130. package/engine/pr-issue-validation.js +653 -0
  131. package/engine/pr-resolve.js +75 -13
  132. package/engine/prd-store.js +39 -6
  133. package/engine/preflight.js +7 -2
  134. package/engine/process-utils.js +79 -31
  135. package/engine/projects.js +198 -12
  136. package/engine/promotion.js +371 -0
  137. package/engine/qa-from-prd.js +3 -0
  138. package/engine/qa-process-validation.js +572 -0
  139. package/engine/qa-runbooks.js +41 -37
  140. package/engine/qa-runners/maestro.js +11 -7
  141. package/engine/qa-runners/playwright.js +37 -14
  142. package/engine/qa-runners.js +9 -10
  143. package/engine/qa-runs.js +124 -11
  144. package/engine/qa-sessions.js +139 -40
  145. package/engine/quarantine-refs.js +81 -11
  146. package/engine/queries.js +163 -9
  147. package/engine/review-learning-backfill.js +482 -0
  148. package/engine/review-learning.js +1236 -0
  149. package/engine/runtimes/claude.js +60 -2
  150. package/engine/runtimes/codex.js +57 -16
  151. package/engine/runtimes/contract.js +21 -0
  152. package/engine/runtimes/copilot.js +51 -3
  153. package/engine/runtimes/index.js +1 -0
  154. package/engine/scheduler.js +201 -32
  155. package/engine/settings-validation.js +909 -0
  156. package/engine/shared.js +705 -208
  157. package/engine/spawn-agent.js +48 -21
  158. package/engine/steering-constraints.js +31 -0
  159. package/engine/steering-store.js +10 -2
  160. package/engine/steering.js +14 -2
  161. package/engine/timeout.js +404 -111
  162. package/engine/untrusted-fence.js +24 -10
  163. package/engine/watch-actions.js +7 -2
  164. package/engine/watches.js +353 -51
  165. package/engine/work-item-validation.js +561 -0
  166. package/engine/work-items-store.js +61 -1
  167. package/engine/worktree-gc.js +656 -37
  168. package/engine/worktree-preflight.js +154 -0
  169. package/engine.js +2068 -342
  170. package/package.json +1 -1
  171. package/playbooks/_pr-description-audit.md +88 -41
  172. package/playbooks/build-fix-complex.md +3 -3
  173. package/playbooks/docs.md +2 -2
  174. package/playbooks/fix.md +18 -18
  175. package/playbooks/implement.md +18 -18
  176. package/playbooks/qa-session-draft.md +10 -12
  177. package/playbooks/qa-session-execute.md +16 -12
  178. package/playbooks/qa-validate.md +8 -7
  179. package/playbooks/shared-rules.md +45 -0
  180. package/playbooks/test.md +4 -0
  181. package/prompts/cc-system.md +1 -1
  182. package/skills/check-self-authored-review-comment/SKILL.md +2 -2
@@ -16,7 +16,7 @@
16
16
  | `capabilities.promptViaArg` | **`false`** | Stdin works in non-interactive mode and dodges the Windows ARG_MAX (~32 KB) limit that breaks `-p "<long-prompt>"` outright (`CreateProcess` rejects it before Copilot even starts). |
17
17
  | `capabilities.modelDiscovery` | **`true`** | `GET https://api.githubcopilot.com/models` with a `gh auth token` Bearer returns HTTP 200 + a 24-model JSON catalog. |
18
18
  | `capabilities.streaming` | **`true`** | `--stream on` (default) emits `assistant.message_delta` events incrementally; `--stream off` suppresses deltas but the final `assistant.message` always arrives. |
19
- | `capabilities.sessionResume` | **`true`** | `--resume <session-id>` documented, and every `result` event emits `sessionId`. |
19
+ | `capabilities.sessionResume` | **`true`** | `--resume=<session-id>` resumes a session, and every `result` event emits `sessionId`. |
20
20
  | `capabilities.midRunSessionId` | **`false`** | Copilot exposes the resumable session id only on the terminal `result`, so mid-run steering waits for a checkpoint. |
21
21
  | `capabilities.resumePromptCarryover` | **`true`** | Command Center resume turns should prepend the browser's recent Q&A transcript because Copilot's session store is opaque to Minions and can resume without enough conversational context. |
22
22
  | `capabilities.systemPromptFile` | **`false`** | No `--system-prompt-file` flag exists. Inject system prompt via a `<system>` block prepended to stdin. |
@@ -142,20 +142,24 @@ prompt (system block prepended) via stdin. This:
142
142
  The `@<path>` prefix syntax is only documented for `--additional-mcp-config`,
143
143
  not `--prompt`.
144
144
 
145
- ### `buildPrompt(promptText, sysPromptText)` — recommended impl
145
+ ### `buildPrompt(promptText, sysPromptText, opts)` — recommended impl
146
146
 
147
147
  Copilot has no `--system-prompt-file`. Inject the system prompt as a `<system>`
148
148
  block prepended to the user prompt, mirroring the convention used by
149
149
  [Anthropic's tool-use docs](https://docs.anthropic.com/en/docs/agents-and-tools/tool-use):
150
150
 
151
151
  ```js
152
- function buildPrompt(promptText, sysPromptText) {
152
+ function buildPrompt(promptText, sysPromptText, opts = {}) {
153
153
  const user = promptText == null ? '' : String(promptText);
154
- if (!sysPromptText) return user;
155
- return `<system>\n${sysPromptText}\n</system>\n\n${user}`;
154
+ if (opts.sessionId) return user;
155
+ if (sysPromptText == null || sysPromptText === '') return user;
156
+ return `<system>\n${String(sysPromptText)}\n</system>\n\n${user}`;
156
157
  }
157
158
  ```
158
159
 
160
+ Resumed sessions already carry their original system context, so replaying the
161
+ block would duplicate instructions on every turn.
162
+
159
163
  Copilot's native custom-instruction discovery remains enabled, so repository
160
164
  instructions augment the Minions task prompt as they do in normal CLI usage.
161
165
 
@@ -174,16 +178,16 @@ Empirically confirmed flags for non-interactive Copilot invocations:
174
178
  | `--autopilot` | for multi-turn agency | Enables `task_complete`-driven multi-turn loop. **Without it** the session ends after one assistant response (see §3.1). |
175
179
  | `--log-level error` | recommended | Suppresses INFO/DEBUG diagnostics that aren't part of the JSONL stream. |
176
180
  | `--disable-builtin-mcps` | gated by config | Disables `github-mcp-server`. Default-on for Minions (`copilotDisableBuiltinMcps: true`) to prevent split-brain PR creation. |
177
- | `--no-color` | optional | Cosmetic; safe to omit when `--output-format json`. |
178
- | `--plain-diff` | optional | Cosmetic; the agent's diff rendering doesn't appear in JSONL stream anyway. |
181
+ | `--no-color` | always emitted | Keeps any incidental CLI rendering free of ANSI color. |
182
+ | `--plain-diff` | always emitted | Keeps incidental diff rendering plain even though normal output is JSONL. |
179
183
  | `--max-autopilot-continues N` | optional | Maps from `opts.maxTurns`. |
180
184
  | `--effort <level>` | optional | Choices: `low|medium|high|xhigh`. **No `max`** — adapter must map `'max' → 'xhigh'`. |
181
185
  | `--model <id>` | optional | Full model ID (see §6 for the catalog). |
182
- | `--resume=<session-id>` | optional | Maps from `opts.sessionId`. Note the `=` syntax `--resume <id>` is also accepted but `--resume` standalone enters interactive picker. |
186
+ | `--resume=<session-id>` | optional | Maps from `opts.sessionId`. The `=` form is required; a separated value is parsed as a positional argument. |
183
187
  | `--stream on` / `--stream off` | optional | Default is `on`. See §4. |
184
188
  | `--enable-reasoning-summaries` | optional | Maps from `opts.reasoningSummaries`; only Anthropic models populate `assistant.reasoning_delta`. |
185
189
  | `--add-dir <path>` | adapter-supported, normally omitted | Registers extra read-allowed dirs when `opts.addDirs` is supplied. Normal Minions dispatch leaves this empty and relies on Copilot's native cwd/user-config discovery. |
186
- | `--attachment <path>` | injected for image inputs | Passes an image file to the model. Repeatable. The adapter writes each base64 image payload to a temp file, emits one `--attachment` per file, and deletes temps after spawn (W-mqv7324u0021db5d). Gated by `capabilities.imageInput`. |
190
+ | `--attachment <path>` | injected for image inputs | Passes an image file to the model. Repeatable. The adapter writes each base64 image payload inside the call-scoped temp directory and emits one `--attachment` per file; `engine/llm.js` removes the directory after the process settles. Gated by `capabilities.imageInput`. |
187
191
  | `-v` / `--verbose` | **never emit** | Does not exist on Copilot. The Claude adapter emits `--verbose`; the Copilot adapter MUST NOT. |
188
192
 
189
193
  ### 3.1 `--autopilot` vs single-shot
@@ -268,10 +272,11 @@ Captured against three model invocations:
268
272
  | `assistant.message` | ✓ | ✓ | ✓ | no | Authoritative final assistant content for the turn. |
269
273
  | `tool.execution_start` | ✓ | ✓ | ✓ | no | Tool call begin. `data.toolName`, `data.arguments`. |
270
274
  | `tool.execution_complete` | ✓ | ✓ | ✓ | no | Tool call end. `data.success: bool`, `data.result.{content, detailedContent}`. |
271
- | `result` | ✓ | ✓ | ✓ | no | Final aggregate. `data.usage`, `sessionId`, `exitCode`. |
275
+ | `result` | ✓ | ✓ | ✓ | no | Final aggregate. Top-level `usage`, `sessionId`, and `exitCode`. |
272
276
  | `function` | (in stdin-no-`-p` test) | – | – | no | Observed once in an early stdin test; appears to be a meta event for tool invocation. **Treat as ignorable** unless future spike re-confirms its semantics. |
273
277
 
274
- All events share the envelope `{ type, data, id, timestamp, parentId, ephemeral? }`.
278
+ Most events share the envelope `{ type, data, id, timestamp, parentId, ephemeral? }`;
279
+ the terminal `result` keeps `usage`, `sessionId`, and `exitCode` at the top level.
275
280
  `ephemeral: true` marks events that the Copilot UI hides from the persistent
276
281
  session log (e.g., deltas, MCP loading noise). The parser should ignore the
277
282
  `ephemeral` flag — it's a UI hint, not a parser hint.
@@ -597,13 +602,14 @@ When implementing `engine/runtimes/copilot.js`:
597
602
  - Recognize npm shims returned by PATH and resolve the package's JavaScript
598
603
  entry; no separate npm-prefix scan is needed.
599
604
  3. `buildArgs(opts)` always emits:
600
- `--output-format json -s --allow-all --no-ask-user --autopilot --log-level error`
605
+ `--output-format json -s --no-color --plain-diff --allow-all --no-ask-user --autopilot --log-level error`
601
606
  plus the conditional flags from §3, plus `--disable-builtin-mcps` per
602
607
  `opts.disableBuiltinMcps`,
603
608
  plus `--attachment <path>` (repeatable) for each image in `opts.images` (§10a).
604
609
  **Never** emit `--verbose`.
605
- 4. `buildPrompt()` injects `<system>...</system>\n\n` block when sysprompt is
606
- non-empty; passthrough otherwise (§2).
610
+ 4. `buildPrompt()` injects `<system>...</system>\n\n` when sysprompt is
611
+ non-empty on a fresh call; a resumed call (`opts.sessionId`) passes through
612
+ only the new user prompt (§2).
607
613
  5. `resolveModel()` translates Minions internal aliases before the CLI boundary:
608
614
  `'haiku'` → `'claude-haiku-4.5'`, `'sonnet'` → `'claude-sonnet-4.5'`, and
609
615
  `'opus'` → `'claude-opus-4.5'`. All other model IDs pass through unchanged.
@@ -646,16 +652,20 @@ When the spike's findings disagree with the plan text, **this document wins**
646
652
  `capabilities.imageInput: true`. The Copilot CLI accepts image files via
647
653
  `--attachment <path>` (the flag is repeatable). The adapter's `buildArgs(opts)`
648
654
  materializes each base64 payload from `opts.images` to a temp file in
649
- `opts.tmpDir`, appends `--attachment <path>` per file, and arranges cleanup after
650
- `spawn` returns.
655
+ `opts.tmpDir` and appends `--attachment <path>` per file. `engine/llm.js`
656
+ removes that call-scoped directory after the process settles.
651
657
 
652
658
  ```js
653
- // Simplified adapter flow (see engine/runtimes/copilot.js _buildAttachmentArgs)
654
- for (const img of opts.images ?? []) {
655
- const ext = MIME_TO_EXT[img.mimeType] ?? 'bin';
656
- const filePath = path.join(opts.tmpDir, `attachment-${i}.${ext}`);
657
- fs.writeFileSync(filePath, Buffer.from(img.dataBase64, 'base64'));
658
- args.push('--attachment', filePath);
659
+ // Simplified buildArgs flow (see engine/runtimes/copilot.js)
660
+ if (Array.isArray(opts.images) && opts.tmpDir) {
661
+ for (let i = 0; i < opts.images.length; i++) {
662
+ const img = opts.images[i];
663
+ if (!img?.dataBase64) continue;
664
+ const ext = _mimeToExt(img.mimeType);
665
+ const filePath = path.join(opts.tmpDir, `img-${i}-${Date.now().toString(36)}.${ext}`);
666
+ fs.writeFileSync(filePath, Buffer.from(img.dataBase64, 'base64'));
667
+ args.push('--attachment', filePath);
668
+ }
659
669
  }
660
670
  ```
661
671
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > A cross-repo plan is a single Minions plan whose work items ship into **two or more configured projects**. The plan has no owning project; each `missing_features` item declares its own `project`, the materializer fans items into per-project SQL scopes, and verify completion fans out into one verify work item per touched repo.
4
4
  >
5
- > Shipped by `PL-cross-repo-coordination` (`prd/minions-opg-2026-06-10-3.json`, items `P-7a3f1c08` … `P-8b5d3e07`). Default plan shape is unchanged for single-project plans — everything in this doc only activates when the plan markdown declares more than one project.
5
+ > Shipped by `PL-cross-repo-coordination` (items `P-7a3f1c08` … `P-8b5d3e07`). Default plan shape is unchanged for single-project plans — everything in this doc only activates when the plan markdown declares more than one project.
6
6
 
7
7
  ## When to use a cross-repo plan
8
8
 
@@ -34,13 +34,13 @@ A cross-repo plan is detected by a single signal at parse time: `shared.extractP
34
34
  **Projects:** minions, minions-opg
35
35
  ```
36
36
 
37
- Both forms accept `,`, `|`, or `;` as separators with surrounding whitespace, and strip surrounding quotes. See `engine/shared.js#extractPlanTargetProjects` (line ~3713) for the exact regex.
37
+ Both forms accept `,`, `|`, or `;` as separators with surrounding whitespace, and strip surrounding quotes. See `engine/shared.js:4516-4559` (`extractPlanDeclaredProject` / `extractPlanTargetProjects`) for the exact regexes.
38
38
 
39
39
  The dashboard's Create-Plan modal (P-b51c08af) renders a multi-select project picker. Submitting two or more projects normalizes to a deduped array and `POST /api/plans/create` writes the plan with the plural `**Projects:**` header + HTML comment marker, no singular `**Project:**` line. Selecting zero or one project keeps the legacy singular header (no behavioural change for single-project plans).
40
40
 
41
41
  ### 2. Plan-to-prd dispatch
42
42
 
43
- When the central planner picks up a plan-to-prd work item, `engine.js#discoverCentralWorkItems` (~line 7470) parses both project signals from the plan content and sets `vars.target_projects` on the dispatch when the plan is cross-repo. The `plan-to-prd.md` playbook (P-3b8c40d9) injects a `{{target_projects}}` section that:
43
+ When the central planner picks up a plan-to-prd work item, `engine.js#discoverCentralWorkItems` (`engine.js:10180-10506`) parses both project signals from the plan content and sets `vars.target_projects` on the dispatch when the plan is cross-repo. The `plan-to-prd.md` playbook (P-3b8c40d9) injects a `{{target_projects}}` section that:
44
44
 
45
45
  - Tells the agent to **omit the top-level `"project"` field** on the PRD JSON (or set it to `""`).
46
46
  - Requires **every `missing_features` item to carry an explicit `project`** chosen from the target list.
@@ -92,13 +92,13 @@ The materializer uses one transaction per project scope (`mutateWorkItems(projec
92
92
 
93
93
  ## Cycle detection (runs ONCE over the whole plan)
94
94
 
95
- `detectDependencyCycles(plan.missing_features)` (`engine.js:4638`) runs once over the full PRD `missing_features` set **before** the per-project grouping loop, so a cyclic chain that crosses repos (e.g. `P-aaa1` in `minions` → `P-bbb2` in `minions-opg` → `P-aaa1`) is detected and every cyclic item is skipped at materialization time — neither project's scope receives the cyclic items.
95
+ `detectDependencyCycles(plan.missing_features)` (`engine.js:7084-7097`, called at `engine.js:7557-7564`) runs once over the full PRD `missing_features` set **before** the per-project materialization loop, so a cyclic chain that crosses repos (e.g. `P-aaa1` in `minions` → `P-bbb2` in `minions-opg` → `P-aaa1`) is detected and every reported cyclic item is skipped at materialization time — neither project's scope receives those items.
96
96
 
97
97
  Caveat: `detectDependencyCycles` is a partial DFS — 2-cycles (`A → B → A`) are fully detected, but N-cycles ≥ 3 with distinct nodes only flag the back-edge target and recursing parent. Both legs of a 2-cycle and the canonical back-edge of a longer cycle reliably get skipped. Fixing the partial detector is tracked separately; cross-repo plans inherit the same behaviour as single-project plans.
98
98
 
99
99
  ## Cross-repo `depends_on` (advisory merge)
100
100
 
101
- `item.depends_on` may freely reference a work-item id that lives in another project. The dispatcher in `engine.js#spawnAgent` (~line 2498) partitions resolved dep branches into two buckets via `dep.isCrossRepo`:
101
+ `item.depends_on` may freely reference a work-item id that lives in another project. The dispatcher in `engine.js#spawnAgent` (`engine.js:4457-4658`) partitions resolved dep branches into two buckets via `dep.isCrossRepo`:
102
102
 
103
103
  1. **Same-project deps** — fetched into the dispatcher's `rootDir` and merged into the worktree using the historical loop (with ancestor pruning, preflight conflict detection, ADO bearer-token retry, etc.). A merge failure here is a real failure (`FAILURE_CLASS.MERGE_CONFLICT` after preflight, retryable).
104
104
 
@@ -106,7 +106,7 @@ Caveat: `detectDependencyCycles` is a partial DFS — 2-cycles (`A → B → A`)
106
106
  - Fetches the dep branch into the **dep's** project rootDir (the current project's origin doesn't carry the ref).
107
107
  - Transfer-fetches the tip into the current worktree under the namespaced ref `refs/cross-repo-deps/<branch>` via a filesystem-path remote.
108
108
  - Computes a best-effort tip SHA + changed-file list (vs the dep project's main).
109
- - Appends a `## Cross-repo dependencies` section to the prompt (`engine.js#buildCrossRepoDepsSection`, ~line 671) listing the repo, branch, short SHA, ref, and changed files.
109
+ - Appends a `## Cross-repo dependencies` section to the prompt (`engine.js:793-823`, `buildCrossRepoDepsSection`) listing the repo, branch, short SHA, ref, and changed files.
110
110
  - **Never merges** the dep branch into the worktree (cross-repo merges would commingle unrelated repo content).
111
111
  - **Never escalates** on fetch failure — the dispatch proceeds with an advisory entry that records the attempt with `files: []`.
112
112
  - Cross-repo deps with no resolvable `projectRoot` (missing `localPath` on the dep project) are dropped with a warn and never reach the agent.
@@ -115,7 +115,7 @@ The agent's contract with cross-repo deps is read-only inspection (`git show ref
115
115
 
116
116
  ## Shared-branch pre-creation (per project, idempotent)
117
117
 
118
- When a cross-repo plan declares `branch_strategy: "shared-branch"` with an explicit `feature_branch`, the materializer's tail block in `engine.js#materializePlansAsWorkItems` (~line 5318) pre-creates that branch in **every project the plan touched** before the first dispatcher pass:
118
+ When a cross-repo plan declares `branch_strategy: "shared-branch"` with an explicit `feature_branch`, the materializer's tail block in `engine.js#materializePlansAsWorkItems` (`engine.js:7718-7756`) pre-creates that branch in **every project the plan touched** before the first dispatcher pass:
119
119
 
120
120
  ```text
121
121
  for each project P in itemsByProject (excluding the `_central` bucket):
@@ -136,24 +136,24 @@ This is what makes the per-project dispatcher's later `git worktree add ... orig
136
136
 
137
137
  `engine.js#discoverFromWorkItems(config, project)` reads exactly one project SQL scope per call, routes pending items via `routing.md`, and returns `newWork[]` entries for the dispatcher. Cross-repo plans don't change this loop; the engine iterates all configured projects per tick.
138
138
 
139
- Module-level `_claimedAgents` in `engine/routing.js` persists across in-process calls but is cleared per tick (`routing.resetClaims()`), so two items in different projects routed to the same agent in the same tick contend correctly for the single agent slot.
139
+ Module-level `_claimedAgents` in `engine/routing.js` persists across in-process calls but is cleared at the start of each discovery pass (`routing.resetClaimedAgents()`), so two items in different projects routed to the same agent in the same pass contend correctly for the single agent slot.
140
140
 
141
141
  ## Per-project verify fan-out
142
142
 
143
- `engine/lifecycle.js#checkPlanCompletion` (line 24, fan-out at lines 217–274) gates on two conditions:
143
+ `engine/lifecycle.js#checkPlanCompletion` (`engine/lifecycle.js:26-155`, fan-out at `:279-494`) gates on two conditions:
144
144
 
145
145
  1. Every PRD `missing_features` item has been materialized into a WI.
146
- 2. Every materialized WI is in a terminal state (`DONE_STATUSES` or `cancelled`).
146
+ 2. Every materialized WI is in `PLAN_TERMINAL_STATUSES` (`DONE_STATUSES`, `failed`, or `cancelled`).
147
147
 
148
148
  When both gates pass, the function groups **active PRs** by project from the SQL PR store and joins them to done items through the `pr_links` table. The result is a `touchedProjects` array: every project that has at least one active PR linked to one of the plan's done items.
149
149
 
150
- For each touched project the fan-out (lines 245–415):
150
+ For each touched project the fan-out (`engine/lifecycle.js:312-494`):
151
151
 
152
152
  - Looks up any existing verify WI keyed on `(sourcePlan, itemType:'verify', project)`. Legacy single-WI plans match against the primary project name when `existingVerify.project` is unset.
153
153
  - Skips if the existing verify is active; re-opens if it's eligible (`isReopenableVerify`).
154
154
  - Otherwise builds a per-project setup block:
155
- - **Shared-branch plans** reuse `feature_branch` directly the verify agent checks out the branch in-place in the project's localPath.
156
- - **Parallel-branch plans** merge each linked PR branch into a per-project worktree at `<localPath>/../worktrees/verify-<projName>-<planSlug>`.
155
+ - **Shared-branch plans** create or reuse a stable sibling verify worktree at `verify-<project>-<plan>` (no collision suffix) from `origin/<feature_branch>`; no PR branches are merged. The fixed name allows coordinated reuse across retries.
156
+ - **Parallel-branch plans** merge each linked PR branch into a per-project worktree at `<localPath>/../worktrees/verify-<projName>-<planSlug>-<uid>` (a `shared.uid()` collision-avoidance suffix) since each item links its own PR and may require unique setup.
157
157
  - Validates every branch ref through `shared.validateGitRef` before splicing into the bash setup commands (P-f3-verify-prompt — defense in depth against shell injection from a crafted `feature_branch` or PR `branch` field).
158
158
  - Writes a new verify WI into the touched project's SQL scope with `itemType: 'verify'`, `type: 'verify'`, `priority: 'high'`, `status: 'pending'`, and `project: <projName>`. The title is project-prefixed (`Verify plan (minions-opg): …`) when `touchedProjects.length > 1` to disambiguate the dashboard listing.
159
159
 
@@ -214,7 +214,7 @@ Plan markdown `plans/cross-repo-launch.md`:
214
214
  Add a new compliance check + matching engine plumbing.
215
215
  ```
216
216
 
217
- Operator approves the plan. Plan-to-prd dispatches against the `minions` project (read-only worktree, first in the target list). The plan-to-prd agent writes PRD `prd/cross-repo-launch.json`:
217
+ Operator approves the plan. Plan-to-prd dispatches against the `minions` project (read-only worktree, first in the target list). The agent writes `agents/<agent-id>/prd-result.json`; the engine validates the envelope, imports its `prd` object into SQLite under filename `cross-repo-launch.json`, and deletes the sidecar. The imported PRD shape is:
218
218
 
219
219
  ```json
220
220
  {
@@ -238,11 +238,11 @@ Operator approves the plan. Plan-to-prd dispatches against the `minions` project
238
238
  }
239
239
  ```
240
240
 
241
- Engine ticks once:
241
+ Engine begins execution:
242
242
 
243
243
  1. `materializePlansAsWorkItems` groups by project: `{ minions: [P-001], minions-opg: [P-002] }`. Cycle scan runs once over both items, finds nothing. Writes P-001 and P-002 to their respective SQL scopes. Branch-strategy is `parallel`, so no shared-branch pre-create runs.
244
244
  2. `discoverFromWorkItems(config, minions)` picks up P-001, routes it via `routing.md`, dispatches `implement` agent. Worktree created on `work/P-001` off `minions`' main.
245
- 3. `discoverFromWorkItems(config, minions-opg)` picks up P-002. Its `depends_on: ["P-001"]` resolves to a same-project dep? **No** P-001 lives in the `minions` project, so it's a **cross-repo dep**. The dispatcher fetches P-001's branch into the `minions` rootDir, transfer-fetches it into the `minions-opg` worktree under `refs/cross-repo-deps/work/P-001`, computes a short SHA + changed-file list, and appends a `## Cross-repo dependencies` section to the prompt. The agent inspects via `git show` / `git log` and ships matching code in `minions-opg`.
245
+ 3. While P-001 is pending or dispatched, P-002 stays pending with `dependency_unmet`. After P-001 reaches a `DONE_STATUSES` value, a later `discoverFromWorkItems(config, minions-opg)` pass picks up P-002. Its dependency lives in the `minions` project, so it is a **cross-repo dep**: the dispatcher fetches P-001's branch into the `minions` rootDir, transfer-fetches it into the `minions-opg` worktree under `refs/cross-repo-deps/work/P-001`, computes a short SHA + changed-file list, and appends a `## Cross-repo dependencies` section to the prompt. The agent inspects via `git show` / `git log` and ships matching code in `minions-opg`.
246
246
  4. Both items complete with PRs. SQL `pr_links` rows link each PR to its WI.
247
247
  5. Next tick `lifecycle.checkPlanCompletion` runs. Gates pass (every item materialized + done). Groups active PRs by project: `{ minions: [PR for P-001], minions-opg: [PR for P-002] }`. Creates two verify WIs:
248
248
  - `(minions)` verify WI in the `minions` scope with `project: 'minions'`.
@@ -258,20 +258,20 @@ When you need to change the cross-repo path, the load-bearing seams are:
258
258
 
259
259
  | Concern | File:line | Notes |
260
260
  | --- | --- | --- |
261
- | Parse plan markdown projects | `engine/shared.js:3690` (`extractPlanDeclaredProject`), `engine/shared.js:3713` (`extractPlanTargetProjects`) | Both scan first 80 lines; HTML comment marker preferred over `**Projects:**` fallback. |
262
- | `POST /api/plans/create` array normalization | `dashboard.js` (P-2e9b54d1) | Dedup + per-name validation via `shared.findProjectByName`; 0–1 → singular header, 2+ → plural header + marker. |
263
- | Plan-to-prd dispatch wiring | `engine.js:7470+` (`discoverCentralWorkItems`) | Sets `vars.target_projects` when plan is cross-repo; picks first listed project for the read-only worktree. |
261
+ | Parse plan markdown projects | `engine/shared.js:4516` (`extractPlanDeclaredProject`), `engine/shared.js:4539` (`extractPlanTargetProjects`) | Both scan first 80 lines; HTML comment marker preferred over `**Projects:**` fallback. |
262
+ | `POST /api/plans/create` array normalization | `dashboard.js:13958-14006` | Dedup + per-name validation via `shared.findProjectByName`; 0–1 → singular header, 2+ → plural header + marker. |
263
+ | Plan-to-prd dispatch wiring | `engine.js:10319-10506` (`discoverCentralWorkItems`) | Sets `vars.target_projects` when plan is cross-repo; picks first listed project for the read-only worktree. |
264
264
  | Plan-to-prd playbook contract | `playbooks/plan-to-prd.md` (P-3b8c40d9) | Tells the agent to omit top-level `project`, set per-item `project`, default to `parallel`. |
265
- | Per-project work-item grouping | `engine.js:5151–5181` (`materializePlansAsWorkItems`) | `itemsByProject` map; unknown-project items stamped `_invalidProject`. |
266
- | Cross-repo cycle detection | `engine.js:5183–5192` | One global scan over `plan.missing_features` BEFORE per-project loop. |
267
- | Shared-branch pre-create per project | `engine.js:5318–5356` | Idempotent `rev-parse` → `branch` → `push -u`; per-project failure isolation. |
268
- | Cross-repo dep partitioning + advisory fetch | `engine.js:2495–2682` (`spawnAgent` dep loop) | `isCrossRepo` split; transfer-fetch via filesystem-path remote into `refs/cross-repo-deps/<branch>`. |
269
- | Cross-repo dep prompt section | `engine.js:662–710` (`buildCrossRepoDepsSection`) | Renders one section per dep with repo, branch, SHA, files, local ref. |
270
- | Per-project verify fan-out | `engine/lifecycle.js` (`checkPlanCompletion`) | Group active PRs by project via SQL links; one verify WI per touched project. |
271
- | Verify WI creation | `engine/lifecycle.js:387–411` | `itemType:'verify'`, `project: projName`, project-prefixed title when `touchedProjects.length > 1`. |
272
- | Per-project rollup pills | `dashboard.js#handlePlansList` (P-66b1faec) | Gate `_perProjectProgress.length >= 2`. |
273
- | Per-project verify badges | `dashboard/js/render-plans.js` (P-d7e592b1) | Gate `verifyWis.length >= 2`. |
274
- | `i.projects[]` + plan `_projects` rollup | `engine/queries.js` (P-e8d49105) | Populated when at least one PRD item has a `project` field. |
265
+ | Per-project work-item grouping | `engine.js:7523-7553` (`materializePlansAsWorkItems`) | `itemsByProject` map; unknown-project items stamped `_invalidProject`. |
266
+ | Cross-repo cycle detection | `engine.js:7084-7097`, `engine.js:7557-7564` | One global scan over `plan.missing_features` before the per-project materialization loop. |
267
+ | Shared-branch pre-create per project | `engine.js:7718-7756` | Idempotent `rev-parse` → `branch` → `push -u`; per-project failure isolation. |
268
+ | Cross-repo dep partitioning + advisory fetch | `engine.js:4457-4658` (`spawnAgent` dep loop) | `isCrossRepo` split; transfer-fetch via filesystem-path remote into `refs/cross-repo-deps/<branch>`. |
269
+ | Cross-repo dep prompt section | `engine.js:793-823` (`buildCrossRepoDepsSection`) | Renders one section per dep with repo, branch, SHA, files, local ref. |
270
+ | Per-project verify fan-out | `engine/lifecycle.js:279-494` (`checkPlanCompletion`) | Group active PRs by project via SQL links; one verify WI per touched project. |
271
+ | Verify WI creation | `engine/lifecycle.js:350-410`, `engine/lifecycle.js:445-494` | `itemType:'verify'`, `project: projName`, project-prefixed title when `touchedProjects.length > 1`. |
272
+ | Per-project rollup pills | `dashboard.js:7826-7845`, `dashboard/js/render-plans.js:389-417` | Gate `Object.keys(_perProjectProgress).length >= 2`. |
273
+ | Per-project verify badges | `dashboard/js/render-plans.js:351-417` | Gate `verifyWis.length >= 2`. |
274
+ | `i.projects[]` + plan `_projects` rollup | `engine/queries.js:2442-2444`, `dashboard.js:7813-7826` | Populated when at least one PRD item has a `project` field. |
275
275
 
276
276
  ## Executable spec
277
277
 
@@ -280,7 +280,7 @@ When you need to change the cross-repo path, the load-bearing seams are:
280
280
  1. PRD with per-item `project` fans into per-project SQL scopes (alpha vs beta).
281
281
  2. `discoverFromWorkItems(config, project)` returns only the queried project's items.
282
282
  3. `checkPlanCompletion` creates one verify WI per touched project when each project has at least one active PR linked to a done WI.
283
- 4. A 3-feature cyclic chain that crosses project boundaries (alpha beta alpha) is detected and none of the cyclic items are written.
283
+ 4. A 2-feature cycle across project boundaries is detected and both cycle members are skipped while an independent third item still materializes.
284
284
 
285
285
  Run via `node test/integration/cross-repo-plan-e2e.test.js` (or as part of `npm run test:integration`).
286
286
 
@@ -1511,7 +1511,7 @@
1511
1511
  <dl>
1512
1512
  <dt>engine.inboxConsolidateThreshold</dt><dd>5</dd>
1513
1513
  <dt>engine.memoryEpisodicCapture</dt><dd>false</dd>
1514
- <dt>engine.autoConsolidateMemory</dt><dd>false</dd>
1514
+ <dt>engine.autoConsolidateMemory</dt><dd>true</dd>
1515
1515
  </dl>
1516
1516
  </article>
1517
1517
  <article class="control-card">
@@ -189,21 +189,6 @@
189
189
  "targetRemovalDate": "2026-08-03",
190
190
  "notes": "Introduced by W-mq03l6zh0006f0a1 (Per-org ADO throttle isolation). 60-day window (2 release cycles + buffer) gives the in-flight per-org migration time to land + observe per-org keys on the diagnostics endpoint. Observable live at GET /api/diagnostics/ado-throttle — the endpoint reports a single `global` key while the arg-less shim is still load-bearing, and per-org `<orgBase>` keys once isolation is complete; that key shape is the human-readable signal for whether this shim can retire."
191
191
  },
192
- {
193
- "id": "pr-link-autoObserve-body-param",
194
- "status": "removed",
195
- "removedDate": "2026-06-25",
196
- "description": "Legacy `autoObserve` body parameter on `POST /api/pull-requests/link`. Replaced by canonical `contextOnly` body param (inverse boolean: `autoObserve: false` ⇔ `contextOnly: true`). This was a READ-BRIDGE on the input side only — the handler read `body.contextOnly` first and fell back to `!body.autoObserve` for callers not yet migrated. Removed by M001 (work/M001).",
197
- "code": [
198
- {
199
- "file": "dashboard.js",
200
- "note": "Removed: linkPullRequestForTracking no longer accepts autoObserve in the destructure; resolvedContextOnly is now simply typeof contextOnly === 'boolean' ? contextOnly : false. Route registry params string no longer lists autoObserve?."
201
- }
202
- ],
203
- "deprecated": "2026-06-08",
204
- "targetRemovalDate": "2026-06-25",
205
- "notes": "Removed on 2026-06-25. On-disk sweep confirmed CLEAN on 2026-06-18. autoObserve parameter silently ignored by server after this removal — callers that still send it will see the value dropped with contextOnly defaulting to false."
206
- },
207
192
  {
208
193
  "id": "worktreemode-field-rename",
209
194
  "description": "Legacy `project.worktreeMode` config field (enum 'isolated'|'live'). Consolidated by W-mqiaw974 (issue #241) into a single `project.checkoutMode` field (enum 'worktree'|'live'): the old 'isolated' value became the implicit default 'worktree', and the overlapping/never-shipped 'shared' value was removed. The write side is migrated — buildProjectEntry, dashboard settings POST, and projects.addProject all write `checkoutMode`, and the dashboard settings/merge paths delete any stale `worktreeMode` key on save. What survives is a READ-BRIDGE ONLY: `shared.resolveCheckoutMode(project)` (and the `isLiveCheckoutProject` predicate built on it) reads canonical `checkoutMode` first, then falls back to the legacy `worktreeMode` field ('isolated'→'worktree', 'live'→'live') so an un-migrated config.json keeps dispatching live-checkout projects correctly. `validateCheckoutMode` also silently coerces a submitted legacy 'isolated' value to 'worktree'.",
@@ -231,22 +216,151 @@
231
216
  "code": [
232
217
  {
233
218
  "file": "dashboard.js",
234
- "note": "POST /api/pull-requests/observe handler reads `body.contextOnly` first, then falls back to `!body.observe` for backwards compat."
219
+ "note": "updatePullRequestObserveFlag() reads `contextOnly` first (canonical); when it is not a boolean it falls back to `!observe` for backwards compat with external callers. dashboard/js/render-prs.js's togglePrObserve() (the only in-repo client) now POSTs `contextOnly` exclusively and no longer sends `observe` (P-2b6e4d81)."
235
220
  }
236
221
  ],
237
222
  "deprecated": "2026-06-08",
238
223
  "status": "active",
224
+ "targetRemovalDate": "2026-09-29",
225
+ "notes": "targetRemovalDate set 2026-07-01 (~90 days out, P-2b6e4d81) after confirming zero remaining `body.observe` / `observe:` sends in dashboard client-side JS (grep of dashboard/js/*.js) — the only client caller, render-prs.js's togglePrObserve(), now POSTs `contextOnly` exclusively. Removal scope on/after 2026-09-29: drop the `body.observe` / `!observe` fallback branch in dashboard.js's updatePullRequestObserveFlag, drop the legacy-`observe`-acceptance note from the route registry `params` string, and remove the back-compat assertions in test/unit/pr-observe-toggle.test.js that POST the legacy `observe` field, once no external tooling is confirmed to still send it."
226
+ },
227
+ {
228
+ "id": "discover-review-skills",
229
+ "description": "engine/discover-review-skills.js — standalone module that discovered and surfaced review-skill annotations from agent output. Superseded by the inline skill-extraction logic in engine/lifecycle.js during the skill-system consolidation.",
230
+ "code": [],
231
+ "deprecated": "2026-06-29",
232
+ "status": "removed",
233
+ "removedDate": "2026-06-29",
239
234
  "targetRemovalDate": null,
240
- "notes": "targetRemovalDate intentionally null — unlike the record-field aliases (`_contextOnly`, `_autoObserve`, `_manual`) which carry a 7-day clock, the `observe` body param is documented as a longer-lived back-compat alias. Set targetRemovalDate to a concrete future date once the dashboard UI + any client scripts are confirmed to POST `contextOnly` exclusively. Removal scope when the date is set: drop the `body.observe` fallback in dashboard.js, drop `observe` from the route registry params, and update any client still POSTing `observe`."
235
+ "notes": "engine/discover-review-skills.js does not exist on disk. The functionality was folded into engine/lifecycle.js skill extraction. No callers remain."
241
236
  },
242
237
  {
243
238
  "id": "discover-review-skills-shim",
244
239
  "description": "engine/discover-review-skills.js is a 25-line re-export shim for engine/discover-project-skills.js. Zero production callers; only test files (discover-review-skills.test.js, discover-project-skills.test.js:706) reference it.",
245
240
  "addedDate": "2026-06-24",
241
+ "deprecated": "2026-06-24",
242
+ "status": "removed",
243
+ "removedDate": "2026-06-25",
244
+ "removedPr": "opg-microsoft/minions#406",
245
+ "removedCommit": "051fbb1a308143ddc5690f5230b986fc7c199bff",
246
246
  "targetRemovalDate": "2026-09-01",
247
247
  "removalGate": "No external callers import discover-review-skills.js; test files updated to import from discover-project-skills.js directly.",
248
248
  "removalScope": "Delete engine/discover-review-skills.js, delete test/unit/discover-review-skills.test.js, update test/unit/discover-project-skills.test.js:706 to import from discover-project-skills.js.",
249
249
  "autoRemoveSafe": false,
250
- "notes": "gate: zero test references to discover-review-skills; must update tests to point to discover-project-skills.js before file can be deleted"
250
+ "notes": "Shim and its dedicated test were deleted in PR #406. This entry is retained as a historical registry record per the deprecated.json contract (records are stamped removed, not deleted)."
251
+ },
252
+ {
253
+ "id": "build-and-test-work-type-alias",
254
+ "description": "Read compatibility for hybrid validation work items created with type `build-and-test` before the engine enforced the distinction between canonical work-item types and playbook names. Routing normalizes the legacy value to canonical `test`; liveValidation config validation also normalizes it on save.",
255
+ "code": [
256
+ {
257
+ "file": "engine/routing.js",
258
+ "lines": "normalizeWorkType",
259
+ "note": "Maps legacy work-item type `build-and-test` to WORK_TYPE.TEST."
260
+ },
261
+ {
262
+ "file": "engine/shared.js",
263
+ "lines": "normalizeLiveValidationWorkType",
264
+ "note": "Normalizes persisted liveValidation.type values before routing and validation."
265
+ }
266
+ ],
267
+ "status": "active",
268
+ "targetRemovalDate": null,
269
+ "removalGate": "A state/config sweep across all installations must show no work_items.type or projects[].liveValidation.type value equal to `build-and-test` for 30 consecutive days.",
270
+ "notes": "Do not remove on a calendar alone; old pending SQL work items must remain dispatchable after upgrade."
271
+ },
272
+ {
273
+ "id": "legacy-runtime-named-spawn-flags",
274
+ "description": "Compatibility bridge for pre-adapter-v1 spawn callers. API-v1 runtime invocations carry their semantic options in the opaque `--runtime-options` payload, but built-in adapters still emit the historical named wrapper flags (`--model`, `--max-turns`, `--effort`, `--resume`, budget/bare/fallback and Copilot tuning flags), and spawn-agent.js still parses those flags individually. Keeping both transports allows older in-process callers and wrapper integrations to continue working during the adapter-contract rollout.",
275
+ "code": [
276
+ {
277
+ "file": "engine/runtimes/contract.js",
278
+ "lines": "buildSpawnFlags",
279
+ "note": "Emits the legacy named flag list before appending the canonical opaque `--runtime-options` payload."
280
+ },
281
+ {
282
+ "file": "engine/runtimes/claude.js",
283
+ "lines": "buildSpawnFlags",
284
+ "note": "Claude-specific legacy wrapper flag emitter."
285
+ },
286
+ {
287
+ "file": "engine/runtimes/copilot.js",
288
+ "lines": "buildSpawnFlags",
289
+ "note": "Copilot-specific legacy wrapper flag emitter, including repeatable disabled-MCP flags."
290
+ },
291
+ {
292
+ "file": "engine/runtimes/codex.js",
293
+ "lines": "buildSpawnFlags",
294
+ "note": "Codex-specific legacy wrapper flag emitter."
295
+ },
296
+ {
297
+ "file": "engine/spawn-agent.js",
298
+ "lines": "parseSpawnArgs",
299
+ "note": "Parses each historical named adapter option in addition to the canonical `--runtime-options` payload."
300
+ }
301
+ ],
302
+ "deprecated": "2026-07-13",
303
+ "status": "active",
304
+ "targetRemovalDate": "2026-10-13",
305
+ "removalGate": "Do not remove before three release windows have shipped with adapter API v1. Add or inspect spawn-wrapper telemetry that distinguishes invocations containing `--runtime-options` from named-flags-only invocations; named-flags-only usage must remain at zero across all known engines and integrations for >=30 consecutive days. Also confirm no external tooling invokes engine/spawn-agent.js with named adapter flags.",
306
+ "removalScope": "Delete buildSpawnFlags from the three bundled adapters; simplify contract.buildSpawnFlags to emit only `--runtime <name> --runtime-options <payload>`; remove the individual adapter-option cases from spawn-agent.parseSpawnArgs while retaining `--runtime`, `--runtime-options`, and generic passthrough handling; update the named-flag compatibility assertions in test/unit.test.js, test/unit/auto-recovery.test.js, test/unit/copilot-adapter.test.js, and test/unit/spawn-agent-helpers.test.js.",
307
+ "autoRemoveSafe": false,
308
+ "notes": "The opaque payload is additive, not encrypted. This bridge is deliberately time-bounded but external-caller-gated; reaching the calendar date alone is insufficient."
309
+ },
310
+ {
311
+ "id": "versionless-runtime-adapter-compat",
312
+ "description": "Compatibility path for custom runtime adapters registered before adapter API versioning. registerRuntime() accepts adapters without `apiVersion`, records them as legacy, does not enforce the strict bundled-adapter method surface, passes their custom buildSpawnFlags() the original raw options object, and suppresses the opaque options suffix so their historical argv contract remains unchanged.",
313
+ "code": [
314
+ {
315
+ "file": "engine/runtimes/contract.js",
316
+ "lines": "legacyAdapters, normalizeRuntimeAdapter, buildSpawnFlags",
317
+ "note": "Tracks versionless adapters in a WeakSet and preserves their pre-v1 buildSpawnFlags behavior."
318
+ },
319
+ {
320
+ "file": "engine/runtimes/index.js",
321
+ "lines": "registerRuntime",
322
+ "note": "Public registration defaults to compatibility normalization; bundled adapters opt into strict validation explicitly."
323
+ },
324
+ {
325
+ "file": "test/unit/spawn-agent-helpers.test.js",
326
+ "lines": "versionless custom buildSpawnFlags receives raw options without an opaque suffix",
327
+ "note": "Pins raw-option identity/shape and absence of the API-v1 suffix for legacy custom adapters."
328
+ }
329
+ ],
330
+ "deprecated": "2026-07-13",
331
+ "status": "active",
332
+ "targetRemovalDate": null,
333
+ "removalGate": "Instrument versionless registerRuntime() calls with a deduplicated deprecation signal, then observe zero registrations across all known engines and supported extension/plugin packages for >=90 consecutive days. A source/package sweep must also confirm every maintained custom adapter exports `apiVersion: 1` and satisfies strict contract validation.",
334
+ "removalScope": "Remove the legacyAdapters WeakSet and versionless branch from engine/runtimes/contract.js; require explicit apiVersion on every registration; make registerRuntime strict by default (or expose an explicitly named test-only partial-registration seam); update partial test adapters to declare API v1 or use that test seam; remove the versionless compatibility tests.",
335
+ "autoRemoveSafe": false,
336
+ "notes": "No calendar-only removal: Minions cannot infer how many third-party adapters exist. An explicit unsupported future API version already fails loudly; this shim covers only an omitted version."
337
+ },
338
+ {
339
+ "id": "preapprove-workspace-mcps-wrapper",
340
+ "description": "Legacy exported helper name from before workspace preparation moved behind the runtime adapter contract. engine/spawn-agent.js still exports preApproveWorkspaceMcps(), which resolves the selected adapter, delegates to prepareWorkspace(), and maps the generic `unsupported-runtime` result back to the historical `not-claude` reason. Production engine dispatch now calls the runtime facade's prepareWorkspace() directly.",
341
+ "code": [
342
+ {
343
+ "file": "engine/spawn-agent.js",
344
+ "lines": "preApproveWorkspaceMcps and module.exports",
345
+ "note": "Backward-compatible wrapper and export; no production engine call site remains."
346
+ },
347
+ {
348
+ "file": "engine/runtimes/claude.js",
349
+ "lines": "prepareWorkspace",
350
+ "note": "Canonical implementation that must remain after the wrapper is removed."
351
+ },
352
+ {
353
+ "file": "test/unit/spawn-agent-helpers.test.js",
354
+ "lines": "preApproveWorkspaceMcps test group",
355
+ "note": "Legacy-wrapper behavioral coverage to migrate to direct Claude-adapter prepareWorkspace tests."
356
+ }
357
+ ],
358
+ "deprecated": "2026-07-13",
359
+ "status": "active",
360
+ "targetRemovalDate": "2026-10-13",
361
+ "removalGate": "After at least three release windows, repository and supported-extension searches must show zero imports or calls to preApproveWorkspaceMcps outside its compatibility tests for >=30 consecutive days. Confirm all maintained callers resolve an adapter and invoke the runtime facade's prepareWorkspace() instead.",
362
+ "removalScope": "Delete preApproveWorkspaceMcps and its module.exports entry from engine/spawn-agent.js; migrate the wrapper tests in test/unit/spawn-agent-helpers.test.js to call engine/runtimes/claude.js#prepareWorkspace directly; retain the adapter implementation and engine.js facade call.",
363
+ "autoRemoveSafe": false,
364
+ "notes": "The wrapper is synchronous and behavior-preserving, so there is no urgency to remove it before external usage is known to be zero."
251
365
  }
252
366
  ]
@@ -134,14 +134,15 @@ curl -s "http://localhost:7331/api/diagnostics/memory/history?process=dashboard&
134
134
  }
135
135
  ```
136
136
 
137
- `samples` is ordered oldest-first. `limit` clamps to the **newest** N.
138
- Both rings are in-process restarting the dashboard zeroes them. There
137
+ `samples` is ordered oldest-first. `limit` must be an integer from 1 to
138
+ 1440 and selects the **newest** N. Unknown or repeated query fields return
139
+ 400. Both rings are in-process — restarting the dashboard zeroes them. There
139
140
  is no on-disk replay for history (by design — the soak gate
140
141
  `test/perf/soak.test.js` covers the regression case the disk history
141
142
  would otherwise serve).
142
143
 
143
- A 400 is returned when `process` is missing or not in
144
- `{engine, dashboard}`.
144
+ A 400 is returned when `process` is missing or not exactly one of
145
+ `{engine, dashboard}`, or when `limit` is malformed or outside its range.
145
146
 
146
147
  ## 2. `MEMORY_BASELINE engine ...` log lines
147
148
 
@@ -221,6 +222,9 @@ curl -X POST -s \
221
222
  | jq
222
223
  ```
223
224
 
225
+ The confirmation is a query field. The POST body must be absent; putting the
226
+ token in a JSON body is rejected before either process captures a snapshot.
227
+
224
228
  ```json
225
229
  {
226
230
  "dashboardSnapshot": "D:\\squad-opg\\engine\\diagnostics\\heap-dashboard-2026-06-12T20-15-30-123Z.heapsnapshot",
@@ -64,7 +64,7 @@ These run continuously without you:
64
64
  You kick these off, then they run without you:
65
65
 
66
66
  - **Work items** — type in dashboard, engine dispatches, agent executes, PR created
67
- - **PRD items** — `/prd` in dashboard, engine discovers and dispatches implement tasks
67
+ - **PRD items** — approve a plan or add an item from **Plans**; the engine materializes and dispatches its work item
68
68
  - **Fan-out** — one task dispatched to all idle agents in parallel
69
69
  - **Retry** — click retry on failed item, engine re-dispatches fresh
70
70
  - **Notes** — `/note` in dashboard, flows through inbox → consolidation → team knowledge
@@ -96,7 +96,7 @@ These are entirely on you:
96
96
  If you start the engine and dashboard, then leave:
97
97
 
98
98
  1. Engine ticks every 10 seconds
99
- 2. Discovers pending work items, PRD gaps, PR reviews needed
99
+ 2. Discovers pending work items, materializable PRD items, and PR reviews needed
100
100
  3. Dispatches agents (up to max concurrent)
101
101
  4. Agents create worktrees, write code, create PRs
102
102
  5. Engine monitors for process exit, hard timeouts, stale orphans, and build failures