@yemi33/minions 0.1.2423 → 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 (184) 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 +12 -7
  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-auto-fix-dispatch.md +3 -3
  64. package/docs/pr-review-fix-loop.md +6 -6
  65. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
  66. package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
  67. package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
  68. package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
  69. package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
  70. package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
  71. package/docs/qa-runbook-lifecycle.md +67 -51
  72. package/docs/qa-runbooks.md +2 -2
  73. package/docs/runtime-adapters.md +101 -50
  74. package/docs/security.md +28 -0
  75. package/docs/self-improvement.md +47 -13
  76. package/docs/skills.md +29 -0
  77. package/docs/slim-ux/concepts.md +2 -2
  78. package/docs/specs/agent-configurability.md +43 -41
  79. package/docs/specs/agent-rename.md +13 -13
  80. package/docs/team-memory.md +283 -14
  81. package/docs/tutorials/03-plan-driven-feature.md +3 -3
  82. package/docs/tutorials/04-runtimes-and-harness.md +18 -21
  83. package/docs/tutorials/08-operations-and-recovery.md +3 -2
  84. package/docs/tutorials/README.md +4 -3
  85. package/docs/visual-evidence-ci.md +103 -0
  86. package/docs/watches.md +35 -30
  87. package/docs/worktree-lifecycle.md +151 -13
  88. package/engine/acp-transport.js +1 -0
  89. package/engine/ado.js +80 -33
  90. package/engine/agent-api-validation.js +571 -0
  91. package/engine/agent-worker-pool.js +7 -6
  92. package/engine/api-contracts/agent-content.js +761 -0
  93. package/engine/api-contracts/cc-ops.js +219 -0
  94. package/engine/api-contracts/config-runtime.js +419 -0
  95. package/engine/api-contracts/core.js +138 -0
  96. package/engine/api-contracts/index.js +506 -0
  97. package/engine/api-contracts/orchestration.js +602 -0
  98. package/engine/api-contracts/pull-requests.js +323 -0
  99. package/engine/api-contracts/qa-process.js +764 -0
  100. package/engine/api-contracts/work-plan-prd.js +494 -0
  101. package/engine/api-validation.js +543 -0
  102. package/engine/cc-api-validation.js +586 -0
  103. package/engine/cc-worker-pool.js +9 -1
  104. package/engine/cleanup.js +393 -271
  105. package/engine/cli.js +65 -7
  106. package/engine/comment-classifier.js +70 -5
  107. package/engine/consolidation.js +123 -5
  108. package/engine/content-api-validation.js +606 -0
  109. package/engine/create-pr-worktree.js +22 -1
  110. package/engine/db/migrations/027-review-learning-lifecycle.js +162 -0
  111. package/engine/dispatch.js +33 -0
  112. package/engine/distribution.js +189 -0
  113. package/engine/execution-model.js +59 -0
  114. package/engine/features.js +12 -0
  115. package/engine/github.js +67 -25
  116. package/engine/harness-context.js +313 -0
  117. package/engine/inbox-store.js +1 -1
  118. package/engine/kb-sweep.js +161 -38
  119. package/engine/keep-process-sweep.js +43 -3
  120. package/engine/lifecycle.js +1066 -165
  121. package/engine/llm.js +4 -0
  122. package/engine/managed-spawn.js +463 -48
  123. package/engine/meeting.js +197 -34
  124. package/engine/memory-retrieval.js +280 -0
  125. package/engine/memory-store.js +821 -38
  126. package/engine/model-discovery.js +17 -3
  127. package/engine/pipeline.js +802 -14
  128. package/engine/plan-prd-validation.js +745 -0
  129. package/engine/playbook.js +206 -17
  130. package/engine/pooled-agent-process.js +6 -1
  131. package/engine/pr-action.js +1 -1
  132. package/engine/pr-issue-validation.js +653 -0
  133. package/engine/pr-resolve.js +75 -13
  134. package/engine/prd-store.js +39 -6
  135. package/engine/preflight.js +7 -2
  136. package/engine/process-utils.js +79 -31
  137. package/engine/projects.js +198 -12
  138. package/engine/promotion.js +371 -0
  139. package/engine/qa-from-prd.js +3 -0
  140. package/engine/qa-process-validation.js +572 -0
  141. package/engine/qa-runbooks.js +41 -37
  142. package/engine/qa-runners/maestro.js +11 -7
  143. package/engine/qa-runners/playwright.js +37 -14
  144. package/engine/qa-runners.js +9 -10
  145. package/engine/qa-runs.js +124 -11
  146. package/engine/qa-sessions.js +139 -40
  147. package/engine/quarantine-refs.js +81 -11
  148. package/engine/queries.js +163 -9
  149. package/engine/review-learning-backfill.js +482 -0
  150. package/engine/review-learning.js +1236 -0
  151. package/engine/runtimes/claude.js +60 -2
  152. package/engine/runtimes/codex.js +57 -16
  153. package/engine/runtimes/contract.js +21 -0
  154. package/engine/runtimes/copilot.js +51 -3
  155. package/engine/runtimes/index.js +1 -0
  156. package/engine/scheduler.js +201 -32
  157. package/engine/settings-validation.js +909 -0
  158. package/engine/shared.js +705 -208
  159. package/engine/spawn-agent.js +48 -21
  160. package/engine/steering-constraints.js +31 -0
  161. package/engine/steering-store.js +10 -2
  162. package/engine/steering.js +14 -2
  163. package/engine/timeout.js +404 -111
  164. package/engine/untrusted-fence.js +24 -10
  165. package/engine/watch-actions.js +7 -2
  166. package/engine/watches.js +353 -51
  167. package/engine/work-item-validation.js +561 -0
  168. package/engine/work-items-store.js +61 -1
  169. package/engine/worktree-gc.js +656 -37
  170. package/engine/worktree-preflight.js +154 -0
  171. package/engine.js +2068 -342
  172. package/package.json +1 -1
  173. package/playbooks/_pr-description-audit.md +88 -41
  174. package/playbooks/build-fix-complex.md +3 -3
  175. package/playbooks/docs.md +2 -2
  176. package/playbooks/fix.md +18 -18
  177. package/playbooks/implement.md +18 -18
  178. package/playbooks/qa-session-draft.md +10 -12
  179. package/playbooks/qa-session-execute.md +16 -12
  180. package/playbooks/qa-validate.md +8 -7
  181. package/playbooks/shared-rules.md +45 -0
  182. package/playbooks/test.md +4 -0
  183. package/prompts/cc-system.md +1 -1
  184. package/skills/check-self-authored-review-comment/SKILL.md +2 -2
@@ -0,0 +1,190 @@
1
+ # Internal install & state migration (ISS ProjectFeed-ISS)
2
+
3
+ One command installs or upgrades the internal Minions package
4
+ (`@opg-microsoft/minions`) from the **ISS `ProjectFeed-ISS` Azure Artifacts npm
5
+ registry** using your existing Azure CLI login, then migrates the existing
6
+ runtime in place — preserving config, SQLite state, notes, plans, knowledge, and
7
+ project links.
8
+
9
+ No manually created PAT is required, accepted, or stored.
10
+
11
+ ## One command
12
+
13
+ ```powershell
14
+ # Windows (PowerShell)
15
+ scripts\install-internal-minions.ps1
16
+ ```
17
+
18
+ ```bash
19
+ # Linux / macOS
20
+ ./scripts/install-internal-minions.sh
21
+ ```
22
+
23
+ ```bash
24
+ # Any platform, directly (also works from an installed package root)
25
+ node bin/install-internal-minions.js
26
+ ```
27
+
28
+ All three entry points are thin wrappers over `bin/install-internal-minions.js`,
29
+ so behavior cannot drift between platforms.
30
+
31
+ ## Prerequisites
32
+
33
+ | Requirement | Check | Notes |
34
+ |---|---|---|
35
+ | Azure CLI, signed in | `az account show` | Run `az login` first. The script acquires a short-lived token per run. |
36
+ | Node.js >= 22.5 with npm | `node -v` / `npm -v` | Matches `package.json#engines` (the `node:sqlite` runtime backing the state DB). |
37
+ | Read access to `ProjectFeed-ISS` | The script's own `validate-feed` step | Feed access is proven **before** anything is removed or replaced. |
38
+
39
+ ## Options
40
+
41
+ | Flag | Default | Purpose |
42
+ |---|---|---|
43
+ | `--version <spec>` | `latest` | Version or dist-tag to install. Resolved to a concrete version before install, so a stale npm metadata cache cannot silently reinstall an older build. |
44
+ | `--registry <url>` | the ISS `ProjectFeed-ISS` feed | Override the Azure Artifacts npm registry. Must be `https`. |
45
+ | `--package <name>` | `@opg-microsoft/minions` | Override the internal package name. Must be scoped. |
46
+ | `--backup-dir <dir>` | `<runtime root>/backups/internal-install-<timestamp>` | Where the pre-migration backup is written. |
47
+ | `--keep-public` | off | Leave an existing `@yemi33/minions` global install in place instead of replacing it. |
48
+ | `--no-restart` | off | Sync runtime files but skip the health-verified restart. |
49
+ | `--force` | off | Proceed even while agents are active (see [Active agents](#active-agents)). |
50
+ | `--dry-run` | off | Print the resolved plan and the exact commands; change nothing. |
51
+ | `--help` | — | Usage. |
52
+
53
+ Exit codes: `0` success, `1` failure, `2` usage error, `3` refused because agents
54
+ are active.
55
+
56
+ ## What it does, in order
57
+
58
+ The ordering is the safety contract, not a formality. `planSteps()` in
59
+ `bin/install-internal-minions.js` is the single source of truth and is asserted
60
+ by `test/unit/install-internal-minions.test.js`.
61
+
62
+ 1. **`acquire-token`** — `az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798`
63
+ (the same ADO resource id `engine/ado-token.js` uses).
64
+ 2. **`validate-feed`** — `npm view <package>@<spec> version` against the feed, and
65
+ resolve `latest` to a concrete version. **A feed that is unreachable, or a
66
+ version that does not exist, fails here — before anything is removed.**
67
+ 3. **`download-artifact`** — `npm pack <package>@<resolved-version> --pack-destination <temp>`
68
+ downloads the tarball without installing it. **This is what makes the cutover
69
+ safe:** the artifact is on local disk before the public package is removed, so
70
+ a feed, network, or auth failure during the global install is recoverable
71
+ offline. Skipped only when no install will run (see step 6).
72
+ 4. **`backup-state`** — checkpoints and copies the SQLite state
73
+ (`engine/state-operations.js#backupState`: `PRAGMA wal_checkpoint(TRUNCATE)` +
74
+ `VACUUM INTO`), then copies `config.json`, `routing.md`, `pinned.md`, parks a
75
+ copy of the downloaded tarball beside the backup, and writes a `manifest.json`
76
+ describing the from/to packages, the artifact, and the preserved paths.
77
+ Skipped on a machine with no existing runtime.
78
+ The backup runs against the **runtime's own** `engine/state-operations.js`,
79
+ which `minions init` copies into the runtime root and which is therefore always
80
+ present and version-matched to that runtime's `engine/state.db`. It does not
81
+ depend on a globally installed CLI: `minions init` deliberately excludes `bin/`
82
+ from the runtime-root copy, and the published public package predates the
83
+ `minions state` command. An installed CLI's `minions state backup` is kept only
84
+ as a fallback for runtime roots that predate the SQL store layout.
85
+ 5. **`uninstall-public`** — removes an existing global `@yemi33/minions` so the two
86
+ packages never race for the same `minions` bin shim. **Gated:** it runs only
87
+ once `assessCutoverReadiness()` confirms the internal artifact is downloaded
88
+ (or already installed at the target version) *and* a state backup exists
89
+ whenever there was a runtime to back up. Skipped with `--keep-public`, or when
90
+ no public install is present.
91
+ 6. **`install-internal`** — `npm install -g <package>@<resolved-version>` against the
92
+ feed. **If that fails, the script installs the already-downloaded tarball**
93
+ (`npm install -g <backup-dir>/<package>.tgz` — no registry, no token, so it
94
+ still works when the feed is exactly what failed) rather than leaving the
95
+ machine with no CLI. This is also the only step that (re-)links the `minions`
96
+ bin shim, so it is skipped as idempotent only when the resolved version is
97
+ already installed **and** `minions` already resolves to it **and** no
98
+ `uninstall-public` is planned. `npm uninstall -g` takes the shim with the
99
+ package that owns it, so skipping the install alongside an uninstall would
100
+ remove the command with nothing to restore it.
101
+ 7. **`verify-install`** — re-reads the installed `package.json` from disk and fails
102
+ loudly if npm did not actually land the resolved version.
103
+ 8. **`verify-shim`** — resolves the global npm prefix and proves the `minions`
104
+ command itself dispatches into the internal package (`minions.cmd` /
105
+ `minions.ps1` / the sh shim on Windows, the `<prefix>/bin/minions` symlink
106
+ elsewhere). Owning the package directory is not the same as owning the
107
+ command: a half-finished cutover can leave `@opg-microsoft/minions` installed
108
+ while `minions` still runs the public build. A shim still pointing at
109
+ `@yemi33/minions` is a hard failure with the repair commands printed.
110
+ 9. **`sync-init`** — `minions init --force --skip-start`, the supported runtime
111
+ synchronization. It overwrites `.js`/`.html` runtime files and preserves
112
+ user-modified `.md` files.
113
+ 10. **`restart`** — `minions restart`, which is health-verified
114
+ (`engine/restart-health.js`: PID + HTTP probe). Skipped with `--no-restart`.
115
+ 11. **`cleanup`** — always runs, on success and on every failure path. The
116
+ temporary npm config always goes; the downloaded artifact is retained (and its
117
+ path printed) when the run ended with the machine still needing it.
118
+
119
+ After `sync-init` the script re-checks every runtime path that existed
120
+ beforehand (`config.json`, `engine/state.db`, `notes/`, `notes.md`, `plans/`,
121
+ `knowledge/`, `projects/`, `pinned.md`). A path that existed before and is
122
+ missing after is a hard failure pointing at the backup, not a silent data loss.
123
+
124
+ ## Token handling
125
+
126
+ - The ADO token is acquired **per run** and is short-lived.
127
+ - It is written **only** into a `0600` temporary npm userconfig inside the OS temp
128
+ directory. It never reaches `argv`, the process environment, stdout, the
129
+ runtime root, or the repository.
130
+ - Your existing user `.npmrc` is copied into that temporary config first, so a
131
+ custom `prefix`, proxy, or other global-install setting keeps applying —
132
+ replacing the userconfig wholesale would silently relocate a `-g` install.
133
+ - Every line of child-process output and every error message is passed through a
134
+ redactor that scrubs both the raw token and its base64 form.
135
+ - The temporary config directory is removed in a `finally`, so it is cleaned up on
136
+ success **and** on every failure path.
137
+
138
+ ## Active agents
139
+
140
+ Replacing the runtime package rewrites the engine code that running agents are
141
+ executing. The engine's restart/reattachment contract covers an engine restart —
142
+ it does **not** cover rewriting the files underneath a live dispatch.
143
+
144
+ So the script counts live agent PID files under `<runtime root>/engine/tmp/`
145
+ (both the per-dispatch directory layout and the legacy flat layout, matching
146
+ `engine/shared.js#forEachPidFile`) and **defers with exit code 3** when any are
147
+ alive. Stale PID files from a crashed run do not block anything — each PID is
148
+ liveness-checked.
149
+
150
+ Wait for `minions status` to go idle, or pass `--force` to override.
151
+
152
+ ## Idempotency
153
+
154
+ Re-running is safe. When the resolved version is already installed, `minions`
155
+ already resolves to it, and there is no public package left to remove, the
156
+ download and install steps are skipped and the run still converges the runtime
157
+ (`init --force`) and proves health (`restart`). Nothing is uninstalled twice, and
158
+ the temporary npm config is removed either way.
159
+
160
+ Re-running is also the **repair** path: if a previous run left the internal
161
+ package on disk while `minions` still resolved elsewhere (or nowhere), the plan
162
+ reinstates `download-artifact` + `install-internal` so the shim is re-linked.
163
+
164
+ ## Relationship to `minions update`
165
+
166
+ `minions update` is the in-place updater for whichever package is already
167
+ installed. This script is the **channel bootstrap**: it is what moves a machine
168
+ onto the internal ProjectFeed-ISS channel (including replacing a public
169
+ `@yemi33/minions` install) and what installs the internal package on a machine
170
+ that has no Minions at all. Once a machine is on the internal channel, routine
171
+ upgrades are `minions update` — or this script again, which is idempotent.
172
+
173
+ ## Troubleshooting
174
+
175
+ | Symptom | Cause | Fix |
176
+ |---|---|---|
177
+ | `could not acquire an Azure DevOps token` | Not signed in, or no subscription selected | `az login`, then `az account set --subscription <id>` |
178
+ | `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. |
179
+ | `REFUSED: N agent process(es) are still running` | Active dispatches | Wait for idle, or re-run with `--force` |
180
+ | `expected <pkg>@<v> on disk, found …` | Stale npm metadata cache | `npm cache clean --force`, then re-run |
181
+ | `refusing to uninstall @yemi33/minions — …` | The artifact or the backup is missing | Nothing was removed. Re-run; the message names which precondition failed. |
182
+ | `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. |
183
+ | `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. |
184
+ | Feed install failed after the public package was removed | Feed/network failure mid-install | Handled automatically — the script reinstalls from the tarball it downloaded before the uninstall. If that also fails, the retained artifact path and the offline `npm install -g <tarball>` command are printed. |
185
+
186
+ ## See also
187
+
188
+ - [distribution.md](distribution.md) — publish channels and the fork sync contract
189
+ - [design-state-storage.md](design-state-storage.md) — the SQLite state authority
190
+ - [engine-restart.md](engine-restart.md) — the health-verified restart contract
@@ -0,0 +1,16 @@
1
+ # KB PR #3223 - Cascade Archiving of PRDs on Markdown File Archive
2
+
3
+ > Historical implementation note. PRD persistence moved to SQLite after this
4
+ > change; filesystem reader, sidecar, and structural-test guidance from the
5
+ > original implementation is no longer current.
6
+
7
+ ## Durable contract
8
+
9
+ - `source_plan` is a plan Markdown basename.
10
+ - Plan archive responses retain their documented compatibility fields.
11
+ - PRD archive and cascade updates use `engine/prd-store.js` transactions.
12
+ - Work-item cancellation uses scoped SQL work-item mutations.
13
+
14
+ Use the current `handlePlansArchive` implementation and its behavioral tests as
15
+ the source of truth. Do not reintroduce PRD file scanning or backup-sidecar
16
+ logic.
@@ -0,0 +1,23 @@
1
+ # KB PR #696 – Merge Conflict Resolution in KB-Sweep Documentation
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`.
4
+
5
+ ## Key Findings
6
+
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
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`
13
+
14
+ ## Action Items
15
+
16
+ - When resolving doc citation conflicts, re-verify current line numbers in merged source rather than trusting either branch's reference
17
+
18
+ ## References
19
+
20
+ - PR #696: https://github.com/opg-microsoft/minions/pull/696
21
+ - Merge commit: b91cb386
22
+ - Fix comment: https://github.com/opg-microsoft/minions/pull/696#issuecomment-4905945906
23
+ - Correct citation: `engine/kb-sweep.js:30` (`NORMALIZE_CONCURRENCY = 5`)
package/docs/kb-sweep.md CHANGED
@@ -177,9 +177,9 @@ Sweep liveness is coordinated through `engine/kb-sweep-state.json` so the dashbo
177
177
 
178
178
  The state file plus runner PID liveness is the source of truth; the detached runner has no dashboard-owned in-memory lifecycle (source: [`engine/kb-sweep.js:747-778`](../engine/kb-sweep.js#L747), [`dashboard.js:7642-7654`](../dashboard.js#L7642)). A separate `engine/kb-swept.json` is written after each successful non-dry-run sweep with the human-readable summary shown by the dashboard's "swept N days ago" badge (source: [`engine/kb-sweep.js:1019-1024`](../engine/kb-sweep.js#L1019)).
179
179
 
180
- ## Automatic Periodic Sweep (opt-in)
180
+ ## Automatic Periodic Sweep
181
181
 
182
- The engine tick loop can also auto-spawn the KB sweep without dashboard interaction. Gated by `engine.autoConsolidateMemory` (default `false` — source: [`engine/shared.js`](../engine/shared.js) `ENGINE_DEFAULTS.autoConsolidateMemory`):
182
+ The engine tick loop can also auto-spawn the KB sweep without dashboard interaction. Gated by `engine.autoConsolidateMemory` (default `true`; set `false` to disable — source: [`engine/shared.js`](../engine/shared.js) `ENGINE_DEFAULTS.autoConsolidateMemory`):
183
183
 
184
184
  - When `engine.autoConsolidateMemory: true`, every tick the engine consults `shouldAutoSweep()` from [`engine/kb-sweep.js`](../engine/kb-sweep.js) and, when the 4-hour cadence has elapsed since the last completion, calls `spawnSweepRunnerDetached()` to fire-and-forget a fresh `engine/kb-sweep-runner.js` process (source: [`engine.js`](../engine.js) tick step 2.1).
185
185
  - The inbox→`notes.md` consolidation runs every tick *regardless* of this flag via `consolidateInbox()`; `autoConsolidateMemory` controls **only** the heavier `knowledge/` sweep.
@@ -35,7 +35,7 @@ Set `meta.keep_processes: true` on the WI; agent writes `agents/<id>/keep-pids.j
35
35
  ## Lifecycle
36
36
 
37
37
  - **Validation:** sidecar shape + workdir checked in `onAgentClose`. Failure → non-retryable `failure_class: invalid-keep-processes-{workdir,schema}`.
38
- - **Sweep:** [`engine/keep-process-sweep.js`](../engine/keep-process-sweep.js) reaps at boot and every 30 ticks. Dead PIDs are pruned; expired TTL → process killed via `shared.killGracefully` then `killImmediate`.
38
+ - **Sweep:** [`engine/keep-process-sweep.js`](../engine/keep-process-sweep.js) reaps at boot and every 180 ticks. Dead PIDs are pruned; expired TTL → process killed via `shared.killGracefully` then `killImmediate`.
39
39
  - **No healthcheck:** unlike managed-spawn, `keep_processes` does not gate WI completion on first-healthy. If the child crashes immediately the agent still succeeds — by design (ad-hoc helper semantics).
40
40
 
41
41
  ## Caps
@@ -43,6 +43,13 @@ Set `meta.keep_processes: true` on the WI; agent writes `agents/<id>/keep-pids.j
43
43
  - **PIDs:** ≤5 per sidecar.
44
44
  - **TTL:** ≤1440 min (24h). Default 60.
45
45
  - **Cwd:** must exist, resolve inside a git worktree, and stay at or below the current dispatch root from `MINIONS_AGENT_CWD`. Monorepo subdirs are allowed. In worktree mode, pointing at `project.localPath` is rejected before the wrapper decides which descendants survive. `keep_processes` dispatches use the cold spawn wrapper rather than the ACP pool so descendant ownership remains enforceable.
46
+ - **Manual kill API:** `POST /api/keep-processes/kill` accepts only a safe
47
+ `agentId` and a numeric positive PID declared by a currently valid sidecar;
48
+ rejected requests do not kill a process or rewrite the file. The endpoint
49
+ atomically claims the declaration, verifies the OS process start identity,
50
+ and restores the claimed record if identity changes or termination fails.
51
+ Its filesystem-mtime identity anchor is preserved after both rejected and
52
+ successful partial-PID updates.
46
53
 
47
54
  After acceptance, the engine adds project/work-type/checkout-mode/dispatch-root
48
55
  provenance to the sidecar. Boot sweep uses it to kill and unlink any retained
@@ -18,15 +18,15 @@ The default `worktree` mode spawns each dispatch inside its own git worktree und
18
18
 
19
19
  For these repos, the operator usually already has one canonical checkout that builds correctly. Live-checkout mode lets the agent dispatch into that checkout instead of cloning a side-by-side copy.
20
20
 
21
- ## Contract (six guarantees)
21
+ ## Contract (seven guarantees)
22
22
 
23
- Live mode is opinionated about what the engine will and will not do to the operator's tree. The contract has six guarantees; the engine enforces them by recovering when explicitly configured, otherwise leaving blocked work pending or failing closed.
23
+ Live mode is opinionated about what the engine will and will not do to the operator's tree. The contract has seven guarantees; the engine enforces them by recovering when explicitly configured, otherwise leaving blocked work pending or failing closed.
24
24
 
25
25
  ### 1. Per-project mutating-concurrency cap of 1
26
26
 
27
27
  Only one worktree-requiring dispatch (fix / build-fix-complex / implement / review / test / verify / decompose / setup / docs) runs per live-mode project at a time. Read-only types (meeting / ask / explore / plan / plan-to-prd) are excluded from the cap because they never write to disk.
28
28
 
29
- Implementation: `engine.js` builds a `liveProjectsInUse` set from the active dispatch list at every allocation pass and per-tick re-annotation; pending items whose project is already in the set are skipped with a `skipReason` and re-tried on the next tick. (`engine.js:7198`, `engine.js:7460`.)
29
+ Implementation: `engine.js` builds a `liveProjectsInUse` set from the active dispatch list at every allocation pass and per-tick re-annotation; pending items whose project is already in the set are skipped with a `skipReason` and re-tried on the next tick.
30
30
 
31
31
  ### 2. Dirty-tree policy
32
32
 
@@ -88,7 +88,7 @@ After the clean-tree check, `prepareLiveCheckout`:
88
88
  - Succeeds → branch exists locally → plain `git checkout <branchName>` (no `--force`, no `-B`, no pull, no reset). The operator owns the checkout state; if they have local commits or a stale tip relative to `origin/<branchName>`, those are preserved.
89
89
  - Fails → `git checkout -b <branchName>` (new branch from the **current HEAD**, not `origin/<mainRef>`).
90
90
 
91
- **No `git fetch`, branch from HEAD (issue #226).** Live mode does **not** run a pre-checkout `git fetch origin <mainRef>` and does **not** seed new branches from `origin/<mainRef>`. The operator's checked-out HEAD is the baseline in live mode (the agent works in-place in the existing checkout), so a fetch only adds latency and failure surface. Concretely, on a Scalar/GVFS-managed ADO repo (blobless partial clone), both `git fetch origin <mainRef>` and `git checkout -b <branch> origin/<mainRef>` trigger on-demand blob fetching through a GVFS cache server (`*.gvfscache.dev.azure.com`) that receives no auth in headless mode (`credential.helper` is disabled), so they fail every time. Branching from HEAD avoids the remote-ref dependency entirely. `mainRef` is still accepted and validated for caller-contract stability but is no longer used to seed the branch in live mode. **Worktree (isolated) mode is unaffected** — that path still branches from `origin/<mainRef>` for a clean baseline.
91
+ **No implicit `git fetch`; branch from HEAD (issue #226).** By default, live mode does **not** run a pre-checkout `git fetch origin <mainRef>` and does **not** seed new branches from `origin/<mainRef>`; the only fetch exception is the explicit, safe [`liveCheckoutAutoFreshen`](#6b-opt-in-auto-freshen-livecheckoutautofreshen-w-mrdlcud2000e630e) opt-in. The operator's checked-out HEAD is otherwise the baseline. On a Scalar/GVFS-managed ADO repo, both fetch and `git checkout -b <branch> origin/<mainRef>` can trigger unauthenticated on-demand blob fetching, so branching from HEAD avoids that dependency. `mainRef` remains validated but does not seed the branch. **Worktree mode is unaffected** — it still branches from `origin/<mainRef>`.
92
92
 
93
93
  PR-source live dispatches (`meta.branch` set) and shared-branch live dispatches (`meta.branchStrategy === 'shared-branch'`) flow through the same code path with no special case. If the operator already has a PR branch checked out at a different SHA than `origin/<branch>`, the engine will not fast-forward it — that is the intended contract (see Open Q5 in the source PRD). Resolve manually with `git pull --ff-only` or commit the local divergence first.
94
94
 
@@ -121,7 +121,7 @@ Like the blob-fetch case this is **structural, not transient** — it does *not*
121
121
 
122
122
  Live mode shares one checkout per project. There is no pool to recycle, no quarantine directory, no per-WI subdirectory under the project root. The mutating-concurrency cap (Guarantee 1) is the only isolation mechanism: agents take turns in the same directory.
123
123
 
124
- Pool short-circuits live in `engine.js:1368` and `engine/cleanup.js`; both gate on `worktreePath` truthiness and `!liveMode`, so the borrow / return / orphan-GC paths execute zero git commands when the project is live.
124
+ Pool short-circuits in `engine.js` and `engine/cleanup.js` gate on `worktreePath` truthiness and `!liveMode`, so borrow / return / orphan-GC execute zero git commands for live projects.
125
125
 
126
126
  ### 5. Refuse on mid-operation / detached HEAD (engine never aborts the operator's in-progress op)
127
127
 
@@ -225,7 +225,7 @@ Live-mode agents run **in-place** in the operator's checkout, so when a dispatch
225
225
  - **Fallback notify (only when a safe switch is impossible).** If git declines the plain checkout — most likely because the agent left uncommitted changes a checkout would overwrite — the refusal is **honored**: the tree is left exactly as the agent left it and a deduped `live-checkout-branch-<dispatchId>` inbox alert tells the operator how to switch back manually (`git -C <localPath> checkout <originalRef>`). The engine never forces the switch. An **unexpected** restore error (git missing, repo corruption, a GVFS blob fetch on the switch-back) now also writes this alert, so a non-refusal failure never silently strands the tree.
226
226
  - **Terminal-failure alert.** When the dispatch ends in a non-success terminal state, a deduped `live-checkout-failed-<dispatchId>` inbox alert is written so the operator knows a live-mode run failed inside their own checkout (where any partial work is visible). This is independent of the restore and fires even when the restore itself succeeds.
227
227
 
228
- The core invariant holds end-to-end through restore: **the engine only ever switches branches it never `git reset`s, `git clean`s, or `git stash`es the operator's tree, and never passes `--force`.** The one addition — the self-healing auto-commit only ever runs `git add`/`git commit` on the engine's *own* agent branch, never on operator refs, and never discards.
228
+ **Restore itself** only switches branches and may auto-commit WIP on the engine's agent branch; it never resets, cleans, stashes, or passes `--force`. The configured pre-spawn auto-stash/auto-reset policies are the explicit exceptions described under Guarantee 2.
229
229
 
230
230
  ## Create-PR chip safety (CC-initiated, separate from the dispatch path)
231
231
 
@@ -290,6 +290,10 @@ Hybrid mode routes only selected canonical work-item types on the live checkout;
290
290
 
291
291
  `type` may be a string or array, but every entry must be a live-capable canonical `WORK_TYPE`. `build-and-test` is a playbook alias and is normalized to `test` only for backward compatibility. `autoDispatch` must be boolean and requires `test` in the configured types; the generated WI has type `test`, carries structured `targetPr` / `pr_id` / URL-reference fields, targets the source PR branch, and renders the `build-and-test` playbook. Successful direct PR-backed `fix` and `build-fix-complex` dispatches, including human-feedback fixes, use the dispatch ID as their dedup key and do not add a nonexistent WI dependency.
292
292
 
293
+ #### Automatic validation deduplication
294
+
295
+ `autoDispatchLiveValidationWi` keeps at most one non-terminal validation for a source WI and for a canonical PR revision across the central and project SQL scopes. Generated WIs persist `meta.liveValidationPrId` and, when known, `meta.liveValidationHeadSha`; another completion for the same PR and head is suppressed, while a new head may create a fresh validation. Fix completions prefer verified remote-head evidence and otherwise use the tracked PR head. Legacy validation WIs without a stored head conservatively block duplicates until terminal. The comparison and insert run in one work-item-store transaction.
296
+
293
297
  ### Enabling auto-stash on dirty (config.json)
294
298
 
295
299
  ```jsonc
@@ -351,7 +355,7 @@ The engine has no opinion about local branches; this hygiene is the operator's r
351
355
  Live-checkout mode is deliberately small. These are NOT supported and will not be added:
352
356
 
353
357
  - **No `auto` mode.** The choice between `worktree` and `live` is per-project and operator-set. The engine will not auto-detect submodules / `repo` workspaces and silently switch modes.
354
- - **No auto-stash on dirty refusal _by default_.** Out of the box the engine refuses and exits; it never `git stash`es to "make room" for a dispatch, because that silently mutates the operator's tree and conflates engine state with operator state. The operator can **opt in** per-project (`project.liveCheckoutAutoStash`) or fleet-wide (`engine.liveCheckoutAutoStash`) to auto-stash instead of refusing — see [2b](#2b-opt-in-auto-stash-on-dirty-w-mqtvnnj1000357fa). Even then the engine never **pops** the stash on the operator's behalf. (The `liveCheckoutAutoReset` escape hatch — **OFF by default** as of W-mrawgw4q000a6bff, and even when enabled now only runs as a last-resort fallback *after* auto-stash fails or is disabled — **discards** rather than stashes; set it to `true` per-project / fleet-wide to opt in. See [§2b](#2b-opt-in-auto-reset-on-dirty-livecheckoutautoreset-w-mqvejug6000eeb20).)
358
+ - **Auto-stash is ON fleet-wide by default (W-mrawiym8000c42c9).** Out of the box a dirty tree is `git stash push --include-untracked`'d before dispatch instead of refusing, because the stash is non-destructive and recoverable via `git stash pop` see [2b](#2b-auto-stash-on-dirty-w-mqtvnnj1000357fa). The operator can **opt out** per-project (`project.liveCheckoutAutoStash: false`) or fleet-wide (`engine.liveCheckoutAutoStash: false`) to restore the terminal dirty-tree refusal instead. Even with auto-stash on, the engine never **pops** the stash on the operator's behalf. (The `liveCheckoutAutoReset` escape hatch — **OFF by default** as of W-mrawgw4q000a6bff, and even when enabled now only runs as a last-resort fallback *after* auto-stash fails or is disabled — **discards** rather than stashes; set it to `true` per-project / fleet-wide to opt in. See [§2b](#2b-opt-in-auto-reset-on-dirty-livecheckoutautoreset-w-mqvejug6000eeb20).)
355
359
  - **No concurrent dispatches per project.** The cap is 1; raising it would require per-WI subdirectories, which live mode explicitly does not provide.
356
360
  - **No per-WI subdirectory isolation.** Live mode is one-checkout-per-project by design. If you need isolation, use `checkoutMode: 'worktree'` (the default).
357
361
  - **No per-WI override.** `checkoutMode` is per-project only. There is no `meta.checkoutMode` on a work item that overrides the project setting.
@@ -375,6 +379,7 @@ Live-checkout mode is deliberately small. These are NOT supported and will not b
375
379
  | `engine.js` — `onAgentClose` live-mode restore wiring | Calls `restoreLiveCheckoutAtDispatchEnd` on every terminal result (P-d9e6b2c4). |
376
380
  | `engine/cli.js` — orphan / reattach restore | Fires the dispatch-end restore (via `maybeRestoreLiveCheckoutFromRecord`) from the persisted record on the engine-restart completion path (P-d9e6b2c4; unified PL-live-checkout-reliability-hardening). |
377
381
  | `engine/timeout.js` — `completeFromOutput` + orphan-sweep restore | Fires `maybeRestoreLiveCheckoutFromRecord` when a re-attached live dispatch is reaped post-restart — closes the gap where these paths had no restore wiring (PL-live-checkout-reliability-hardening). |
382
+ | `engine/lifecycle.js` — `autoDispatchLiveValidationWi` | Creates PR-targeted validation WIs and transactionally deduplicates them by source WI plus canonical PR identity/head across central and project scopes. |
378
383
  | `engine.js` — dispatcher `liveProjectsInUse` set | Per-project mutating-concurrency cap (P-a3f9b205). |
379
384
  | `engine.js` — worktree-pool / orphan-GC short-circuits | `worktreePath===null` no-ops in live mode (P-a3f9b206). |
380
385
  | `engine/cleanup.js` — `runPeriodicWorktreeSweep` live filter | Excludes live-checkout projects from the registry-derived periodic worktree GC so the operator's primary checkout never enters the GC decision surface (PL-live-checkout-reliability-hardening). |
@@ -57,7 +57,12 @@ The sidecar lives at `<MINIONS_DIR>/agents/<agentId>/managed-spawn.json` and is
57
57
  }
58
58
  ```
59
59
 
60
- The renderer in [`buildManagedSpawnHint`](../engine/managed-spawn.js) at `engine/managed-spawn.js:419` emits this exact shape (with allowlist + cap reminders) into the agent's prompt whenever the work item has `meta.managed_spawn: true`. Treat the rendered hint as the source of truth — if this doc and the hint drift, the hint wins.
60
+ The renderer in [`buildManagedSpawnHint`](../engine/managed-spawn.js) emits this exact shape (with allowlist + cap reminders) into the agent's prompt whenever the work item has `meta.managed_spawn: true`. Treat the rendered hint as the source of truth — if this doc and the hint drift, the hint wins.
61
+
62
+ For an opted-in dispatch, the sidecar is mandatory. Completion acceptance reads
63
+ only the canonical running agent's directory and requires `written_by` and
64
+ `wi_id` to match that agent and work item exactly; a missing file or stale
65
+ identity fails with `invalid-managed-spawn` instead of completing successfully.
61
66
 
62
67
  The engine also confines every spec to the dispatch root: `cwd` must resolve at
63
68
  or below `MINIONS_AGENT_CWD`. In worktree mode, `project.localPath` is rejected
@@ -176,8 +181,9 @@ Single quotes (`'…'`) work the same way. No `powershell -Command "& '<path>' <
176
181
 
177
182
  ┌───────────────────────────────────────────────────────────────────┐
178
183
  │ 2. evaluateManagedSpawnAcceptance(agentId) │
179
- │ - Validates schema, allowlists, caps
180
- │ - Rejects ERROR + alert + sidecar deleted
184
+ │ - Requires the canonical agent/WI sidecar
185
+ │ - Validates identity, schema, allowlists, caps
186
+ │ - Rejects → ERROR + alert (+ delete when present) │
181
187
  │ failure_class: invalid-managed-spawn (non-retryable) │
182
188
  └───────────────────────────────────┬───────────────────────────────┘
183
189
  │ accepted
@@ -247,6 +253,9 @@ Outcome: if WI 2 fails, the spec stays alive (TTL-managed) and the next dispatch
247
253
  | Dashboard | http://localhost:7331/engine → "Managed Processes" |
248
254
 
249
255
  The list endpoint returns an ETag; pass it as `If-None-Match` on follow-up polls to get `304 Not Modified` when nothing changed. The dashboard panel does this automatically.
256
+ API process names use the same kebab-case/length validator as sidecar specs,
257
+ project filters must name a configured project, and log-stream `tail` is a
258
+ single integer from 1 byte through 1 MiB (default 64 KiB).
250
259
 
251
260
  ### Force a restart / kill
252
261
 
@@ -255,6 +264,15 @@ The list endpoint returns an ETag; pass it as `If-None-Match` on follow-up polls
255
264
  | Kill (PID terminated, removed from state) | `POST /api/managed-processes/kill` `{"name":"…"}` | Per-row "Kill" button |
256
265
  | Restart (kill old PID, respawn from saved state, kick first healthcheck) | `POST /api/managed-processes/restart` `{"name":"…"}` | Per-row "Restart" button |
257
266
 
267
+ Both controls bind the request to the selected row's `pid` + `started_at`
268
+ generation and verify the live OS process start identity before killing. A
269
+ concurrent replacement, recycled PID, or failed kill returns `409` without
270
+ deleting the surviving generation; restart also reserves the name before
271
+ spawning so concurrent calls cannot orphan duplicate generations. Engine batch
272
+ spawns use the same reservation protocol, first-health and boot-probe writes
273
+ must match the reserved generation, and failed rollback kills remain as
274
+ immediately-expired tracked rows for the sweep to retry.
275
+
258
276
  Killing a spec from outside Minions (raw `Stop-Process`) leaves a stale SQL row until the next 30-tick sweep notices the dead PID. Prefer the API.
259
277
 
260
278
  ### View logs
@@ -83,8 +83,8 @@ defaults (`WORKSPACE_MANIFEST_DEFAULTS` in `engine/shared.js`):
83
83
  | `memory_scope` | `'shared'` | One of `private`, `shared`, `read-only-shared`. |
84
84
 
85
85
  Enforcement helpers (also in `engine/shared.js`): `validateWorkspaceManifest`,
86
- `resolveAgentManifest`, `agentCanUseRepo`, `agentCanUseTool`, `agentCanFetchUrl`,
87
- `mergeManifestAllowedTools`. Today the engine actively enforces (a) the
86
+ `resolveAgentManifest`, `agentCanUseRepo`, and `mergeManifestAllowedTools`. Today the
87
+ engine actively enforces (a) the
88
88
  **repo gate** at dispatch time in `engine.js spawnAgent`
89
89
  (`FAILURE_CLASS.WORKSPACE_MANIFEST_REPO`, non-retryable) and (b) the **tool merge** into the
90
90
  runtime `--allowedTools` flag at spawn time. Defaults are permissive — an agent with no
@@ -105,6 +105,7 @@ Routing maps a `work_type` to a `preferred` agent and a `fallback`:
105
105
  | implement:large | rebecca | dallas |
106
106
  | review | ripley | lambert |
107
107
  | fix | `_author_` | `_any_` |
108
+ | build-fix-complex | `_author_` | dallas |
108
109
  | plan | ripley | rebecca |
109
110
  | plan-to-prd | lambert | rebecca |
110
111
  | explore | ripley | rebecca |
@@ -9,12 +9,12 @@ How plans go from idea to verified, running code.
9
9
  → agent writes plan markdown (plans/plan-*.md)
10
10
  → human reviews and explicitly runs plan-to-prd
11
11
  → agent converts plan into structured PRD items with acceptance criteria
12
- → engine materializes PRD items as work items (PL-W001, PL-W002, ...)
12
+ → engine materializes each PRD item under the same id as a work item (P-001, P-002, ...)
13
13
  → engine dispatches work items respecting dependency order
14
14
  → agents implement each item, create PRs
15
15
  → engine auto-reviews PRs, handles feedback loops
16
- → all items done → engine creates VERIFY task
17
- → verify agent builds everything, starts webapp, writes testing guide
16
+ → all items terminal → engine creates VERIFY task(s) for completed work
17
+ → verify agent builds everything, starts a webapp if applicable, writes testing guide
18
18
  → human archives the PRD/plan from the dashboard when ready
19
19
  ```
20
20
 
@@ -26,29 +26,29 @@ PRD items can declare `depends_on: ["P001", "P003"]`. The engine won't dispatch
26
26
 
27
27
  ### Branch Merging at Spawn
28
28
 
29
- When an agent is spawned for a work item with dependencies, the engine merges the dependency PR branches into the worktree **before the agent starts**. This solves the problem where dependencies have PRs but haven't been merged to `main` yet.
29
+ When an agent is spawned for a work item with dependencies, the engine merges same-project dependency PR branches into the worktree **before the agent starts**. Cross-repo dependencies are exposed as advisory refs instead of being merged. This lets later items inspect prerequisite code that has not reached the target repo's main branch.
30
30
 
31
31
  ```
32
- Agent gets work item PL-W006 (plan item P009)
32
+ Agent gets work item P009
33
33
  depends_on: ["P006", "P007"]
34
34
 
35
35
  spawnAgent():
36
36
  1. Create worktree from main:
37
- git worktree add ../worktrees/work/PL-W006 -b work/PL-W006 main
37
+ git worktree add ../worktrees/work/P009 -b work/P009 main
38
38
 
39
39
  2. Resolve dependencies:
40
- P006 → work item PL-W001 → PR-4970100 → branch work/PL-W001
41
- P007 → work item PL-W002 → PR-4970101 → branch work/PL-W002
40
+ P006 → work item P006 → PR-4970100 → branch work/P006
41
+ P007 → work item P007 → PR-4970101 → branch work/P007
42
42
 
43
43
  3. Merge dependency branches:
44
- git fetch origin work/PL-W001 work/PL-W002
45
- git merge origin/work/PL-W001 --no-edit
46
- git merge origin/work/PL-W002 --no-edit
44
+ git fetch origin work/P006 work/P007
45
+ git merge origin/work/P006 --no-edit
46
+ git merge origin/work/P007 --no-edit
47
47
 
48
48
  4. Agent starts with all dependency changes present
49
49
  ```
50
50
 
51
- If a merge conflict occurs, it's logged as a warning. The agent will see conflict markers and can resolve them, or the task will fail and get retried after the dependency PRs are merged to `main`.
51
+ If dependency merging still conflicts after the engine's clean reset/re-merge attempt, the engine aborts the partial merge and fails the dispatch **before spawning the runtime** with `FAILURE_CLASS.MERGE_CONFLICT`. When it can identify the conflicting dependency branch it also queues a conflict-fix work item on that branch; the dependent item waits for the branch to become mergeable.
52
52
 
53
53
  ### Resolution Chain
54
54
 
@@ -57,17 +57,17 @@ depends_on: ["P-43e5ac28"]
57
57
  → find work items where id = "P-43e5ac28" (across all projects)
58
58
  → find PRs where prdItems includes "P-43e5ac28"
59
59
  → extract branch names from those PRs
60
- → fetch + merge into worktree
60
+ → fetch + merge into worktree (same project), or expose an advisory ref (cross-repo)
61
61
  ```
62
62
 
63
63
  ## Plan Completion
64
64
 
65
- When all PRD items reach `done` or `failed` status, `checkPlanCompletion()` fires:
65
+ When every materialized PRD item reaches a `PLAN_TERMINAL_STATUSES` value (`done`/legacy-done, `failed`, or `cancelled`), `checkPlanCompletion()` fires:
66
66
 
67
67
  1. **Completion summary** written to `notes/inbox/` with results, timing, and PR list
68
- 2. **Verification task** created (type `verify`, priority `high`) — see below
68
+ 2. **Verification task(s)** created (type `verify`, priority `high`) when at least one item completed one per touched project; see below
69
69
  3. **PR work item** created for shared-branch plans (to merge the feature branch)
70
- 4. **PRD status** persisted as `completed` with `_completionNotified`; files stay active until manual archive
70
+ 4. **PRD status** persisted as `completed` with `_completionNotified`; the SQL record stays active until manual archive
71
71
 
72
72
  `checkPlanCompletion()` also **self-heals a stale `completed` PRD**: if a completed plan later gains an unmaterialized item or one of its items regresses off a terminal status, the PRD is reverted to `active` (status flipped, `_completionNotified` cleared) so the materializer recreates the missing work item and the normal completion flow can run again.
73
73
 
@@ -75,16 +75,16 @@ When all PRD items reach `done` or `failed` status, `checkPlanCompletion()` fire
75
75
 
76
76
  ## Verification Task
77
77
 
78
- The verification task (`planItemId: "VERIFY"`) is the final step. It:
78
+ The verification task (`itemType: "verify"`, `type: "verify"`) is the final step. It:
79
79
 
80
- 1. **Creates one worktree per affected project** with all PR branches merged in not separate worktrees per branch. This means the agent builds and tests from a single directory per project with all changes combined.
80
+ 1. **Creates one worktree per affected project.** Parallel-branch plans merge that project's PR branches into it; shared-branch plans check out `origin/<feature_branch>` without merging PR branches.
81
81
 
82
82
  2. **Builds and tests** every affected project from its worktree.
83
83
 
84
- 3. **Starts the webapp** on localhost if applicable, and keeps it running so the user can see it.
84
+ 3. **Starts and smoke-checks the app** on localhost when applicable, leaving it running only when it must survive verification.
85
85
 
86
- 4. **Writes a canonical manual testing guide** (`prd/guides/verify-*.md`) plus
87
- an inbox copy (`notes/inbox/verify-*.md`) for merge decisions, containing:
86
+ 4. **Writes a canonical manual testing guide** (`prd/guides/verify-*.md`) and,
87
+ only after successful verification, an inbox copy (`notes/inbox/verify-*.md`) for merge decisions, containing:
88
88
  - Build/test status table per project
89
89
  - Feature-by-feature testing steps based on acceptance criteria
90
90
  - Integration points to verify across projects
@@ -97,20 +97,20 @@ The verification task (`planItemId: "VERIFY"`) is the final step. It:
97
97
  ```
98
98
  # project-a — 3 PRs merged into one worktree
99
99
  cd ~/project-a
100
- git fetch origin "work/PL-W001" "work/PL-W002" "work/PL-W003" "main"
101
- git worktree add "../worktrees/verify-my-plan-2026-03-15" "origin/main"
102
- cd "../worktrees/verify-my-plan-2026-03-15"
103
- git merge "origin/work/PL-W001" --no-edit
104
- git merge "origin/work/PL-W002" --no-edit
105
- git merge "origin/work/PL-W003" --no-edit
100
+ git fetch origin "work/P001" "work/P002" "work/P003" "main"
101
+ git worktree add "../worktrees/verify-project-a-my-plan-<uid>" "origin/main"
102
+ cd "../worktrees/verify-project-a-my-plan-<uid>"
103
+ git merge "origin/work/P001" --no-edit
104
+ git merge "origin/work/P002" --no-edit
105
+ git merge "origin/work/P003" --no-edit
106
106
 
107
107
  # project-b — 2 PRs merged into one worktree
108
108
  cd ~/project-b
109
- git fetch origin "feat/PL-W004" "feat/PL-W005" "master"
110
- git worktree add "../worktrees/verify-my-plan-2026-03-15" "origin/master"
111
- cd "../worktrees/verify-my-plan-2026-03-15"
112
- git merge "origin/feat/PL-W004" --no-edit
113
- git merge "origin/feat/PL-W005" --no-edit
109
+ git fetch origin "work/P004" "work/P005" "master"
110
+ git worktree add "../worktrees/verify-project-b-my-plan-<uid>" "origin/master"
111
+ cd "../worktrees/verify-project-b-my-plan-<uid>"
112
+ git merge "origin/work/P004" --no-edit
113
+ git merge "origin/work/P005" --no-edit
114
114
  ```
115
115
 
116
116
  The verify agent and the user can then build and run from these paths.
@@ -9,10 +9,10 @@ All dispatch decisions happen inside `engine.js#discoverFromPrs` once per discov
9
9
  | # | Cause | Trigger (PR state) | Per-cause gate flag | Hard-stop kill-switch | Other gates |
10
10
  |---|------------------------|---------------------------------------------------|----------------------------|-----------------------|-----------------------------------------------------------|
11
11
  | 1 | Initial minion review | `reviewStatus === 'pending'` | `autoReviewPrs` | `autoFixPaused`* | `evalLoop`; provider polling enabled |
12
- | 2 | Human-feedback fix | `humanFeedback.pendingFix` (or coalesced) | `autoFixHumanComments` | `autoFixPaused` | provider polling enabled; `!awaitingReReview` |
12
+ | 2 | Human-feedback fix | `humanFeedback.pendingFix || hasCoalescedFeedback` | `autoFixHumanComments` | `autoFixPaused` | provider polling enabled; `!fixDispatched` |
13
13
  | 3 | Minion re-review | `reviewStatus === 'waiting'` after fix push | `autoReReviewPrs` | `autoFixPaused`* | `evalLoop`; provider polling enabled |
14
- | 4 | Minion review-feedback fix | `reviewStatus === 'changes-requested'` | `autoFixReviewFeedback` | `autoFixPaused` | `evalLoop`; provider polling enabled; `!awaitingReReview` |
15
- | 5 | Build-failure fix | `buildStatus === 'failing'` + grace expired | `autoFixBuilds` | `autoFixPaused` | provider polling enabled; `_buildFixPushedAt` grace |
14
+ | 4 | Minion review-feedback fix | `reviewStatus === 'changes-requested'` | `autoFixReviewFeedback` | `autoFixPaused` | `evalLoop`; polling; `!awaitingReReview`; `!fixDispatched` |
15
+ | 5 | Build-failure fix | `buildStatus === 'failing'` + grace expired | `autoFixBuilds` | `autoFixPaused` | polling; `_buildFixPushedAt` grace; `!fixDispatched` |
16
16
  | 6 | Merge-conflict fix | `_mergeConflict === true` + `status === 'active'` | `autoFixConflicts` | `autoFixPaused` | provider polling enabled; `!fixDispatched` in same pass |
17
17
 
18
18
  \* `autoFixPaused` is the auto-**fix** kill-switch. It forces sites 2, 4, 5, and 6 off, but is **not** wired to suppress review dispatch (sites 1 and 3) — review verdicts stay fresh during a fix-storm incident. To halt review dispatch, clear `autoReviewPrs` / `autoReReviewPrs` directly, or set `pollingPaused: true` (which forces every per-PR auto-dispatch gate inert by zeroing `pollEnabled`).