@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2424",
3
+ "version": "0.1.2425",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"
@@ -65,56 +65,96 @@ When you skip, still report it: set `meta.descriptionAudit.ran = false` and
65
65
  6. **GET-verify the description post-patch** by re-running the fetch from
66
66
  step 1 and confirming the body matches what you sent.
67
67
 
68
- ## Screenshot handling
69
-
70
- This applies only when the **existing** description embeds markdown image
71
- refs. **Never introduce screenshots to a PR that didn't already have any**
72
- this audit is a refresh, not an introduction.
73
-
74
- 1. Extract every `![alt](url)` from the current description.
75
- 2. For each image, decide whether it depicts a view this dispatch's diff
76
- touched. Heuristics (any one is enough):
77
- - `alt` text or filename mentions a component / route / view name that
78
- appears in the changed files.
79
- - The image URL path contains an issue/PR id matching this PR.
80
- - The description text immediately around the image references files
81
- present in the diff.
82
- 3. If the view was touched **and** the project has a runnable dev server
83
- (detect via `package.json` scripts named `dev`, `start`, or `serve`)
84
- **and** Playwright MCP is available, recapture:
85
- 1. Spin up the dev server with a detached handoff (see
86
- `shared-rules.md` "Long-Running Commands"). Capture PID + log path +
87
- URL + stop command.
88
- 2. Drive Playwright MCP to the relevant route and screenshot the same
89
- view. Save raw PNGs to `agents/<id>/screenshots/` only. **NEVER `git add` a PNG** — adding any code path that commits a screenshot is a
90
- release-blocker bug.
91
- 3. Upload as a PR attachment:
92
- - **ADO**: `PUT /pullRequests/<id>/attachments/<filename>` with the
93
- PNG bytes, then reference the returned URL in the description.
94
- - **GitHub**: there is no documented REST endpoint for PR-comment
95
- image uploads. The browser drag-drop path uses
96
- `github.com/upload/asset`, which `gh` does not wrap. If you cannot
97
- find a reliable scripted upload path, **skip** this screenshot,
98
- leave the old image URL in place, and record
99
- `meta.descriptionAudit.result = "screenshots-refreshed-skipped (gh-attachment-unavailable)"`.
100
- 4. Patch the description to swap old image URLs for new.
101
- 5. Stop the dev server using the recorded stop command.
102
- 4. If the dev server fails to start, the route 404s, Playwright fails, or
103
- the host's attachment API is unreachable → **skip that screenshot**
104
- without failing the work item. Record the skip and the reason in
105
- `meta.descriptionAudit`.
68
+ ## Screenshot handling (introduce AND refresh for visual changes)
69
+
70
+ For a PR that contains a **meaningful visual/UI change**, proactively CAPTURE
71
+ screenshots and EMBED them directly in the PR description do **not** rely only
72
+ on the OPG `Visual evidence capture` CI bot (that bot stays as-is; this is
73
+ additive PR-body evidence). When the existing description already embeds
74
+ `![alt](url)` refs for a view this dispatch touched, refresh them the same way.
75
+
76
+ **Scope guard which changes get screenshots.** Only meaningful visual/UI
77
+ changes: dashboard pages, slim-ux, Settings, work-item/PR/plan views, badges,
78
+ layout/CSS. Skip backend-only, engine-logic, text/prose-only, analysis, and
79
+ docs changes those get NO screenshots (they add no signal). Use a BEFORE/AFTER
80
+ pair for layout/visual FIXES; an AFTER-only shot for NEW UI.
81
+
82
+ **Capture.** If the project has a runnable dev server (detect via
83
+ `package.json` scripts named `dev`, `start`, or `serve`) AND Playwright MCP is
84
+ available:
85
+
86
+ 1. Spin up the dev server with a detached handoff (see `shared-rules.md` →
87
+ "Long-Running Commands"). Record PID + log path + URL + stop command.
88
+ 2. Drive Playwright MCP to the changed route at a normal viewport and
89
+ screenshot the section that changed. Save raw PNGs to
90
+ `agents/<id>/screenshots/` ONLY. **NEVER `git add` a PNG** — committing a
91
+ screenshot is a release-blocker bug.
92
+ 3. Stop the dev server using the recorded stop command.
93
+
94
+ **Upload + embed GitHub (release-asset CDN recipe).** GitHub has no
95
+ documented REST endpoint for PR-comment image uploads, so publish the PNGs as
96
+ assets on a dedicated per-PR prerelease and embed the asset CDN URLs. This works
97
+ on PRIVATE repos and never commits PNGs to the source branch. Resolve the
98
+ correct account token for the repo owner and build a per-command `GH_TOKEN` —
99
+ **NEVER run `gh auth switch`** (`opg-microsoft` → `yemishin_microsoft`,
100
+ `yemi33` `yemi33`).
101
+
102
+ bash:
103
+
104
+ ```bash
105
+ OWNER=opg-microsoft; REPO=minions; PR=<num>; TAG="pr-${PR}-visual"
106
+ GH_TOKEN=$(gh auth token --user yemishin_microsoft --hostname github.com)
107
+ # Create the per-PR prerelease once; on re-dispatch, upload --clobber instead.
108
+ GH_TOKEN=$GH_TOKEN gh release create "$TAG" agents/<id>/screenshots/*.png \
109
+ --repo "$OWNER/$REPO" --title "PR #${PR} visual evidence" --prerelease \
110
+ --notes "Visual evidence for PR #${PR}" \
111
+ || GH_TOKEN=$GH_TOKEN gh release upload "$TAG" agents/<id>/screenshots/*.png \
112
+ --repo "$OWNER/$REPO" --clobber
113
+ # Resolve each asset's public CDN url:
114
+ URL=$(GH_TOKEN=$GH_TOKEN gh api "repos/$OWNER/$REPO/releases/tags/$TAG" \
115
+ --jq '.assets[]|select(.name=="after.png")|.browser_download_url')
116
+ ```
117
+
118
+ PowerShell:
119
+
120
+ ```powershell
121
+ $OWNER='opg-microsoft'; $REPO='minions'; $PR=<num>; $TAG="pr-$PR-visual"
122
+ $env:GH_TOKEN = (gh auth token --user yemishin_microsoft --hostname github.com)
123
+ $pngs = Get-ChildItem agents/<id>/screenshots/*.png
124
+ gh release create $TAG $pngs --repo "$OWNER/$REPO" --title "PR #$PR visual evidence" --prerelease --notes "Visual evidence for PR #$PR"
125
+ if ($LASTEXITCODE -ne 0) { gh release upload $TAG $pngs --repo "$OWNER/$REPO" --clobber }
126
+ $URL = (gh api "repos/$OWNER/$REPO/releases/tags/$TAG" --jq '.assets[]|select(.name=="after.png")|.browser_download_url')
127
+ ```
128
+
129
+ Then embed each asset in the PR body as `![<view> (after)](<url>)` (BEFORE/AFTER
130
+ pair for fixes, AFTER-only for new UI) and patch the description with
131
+ `gh pr edit <num> --repo <owner>/<repo> --body-file <file>`.
132
+
133
+ **Upload + embed — Azure DevOps.** ADO exposes a scriptable attachment API:
134
+ `PUT /pullRequests/<id>/attachments/<filename>` with the PNG bytes, then
135
+ reference the returned URL in the description. This now applies to
136
+ newly-introduced screenshots too, not just refreshes.
137
+
138
+ **Degrade gracefully.** If the dev server won't start, the route 404s,
139
+ Playwright is unavailable, or the upload fails → **SKIP** without failing the
140
+ work item and record the skip + reason in `meta.descriptionAudit` (e.g.
141
+ `result: "screenshots-skipped (dev-server-unavailable)"`). Screenshots NEVER
142
+ land in the repo — `agents/<id>/screenshots/` only; never `git add` a PNG.
106
143
 
107
144
  ## Out-of-scope guardrails
108
145
 
109
- - Don't add screenshots that weren't already there.
146
+ - Only screenshot **meaningful visual/UI changes** (see the scope guard above);
147
+ skip backend/engine/text/analysis/docs changes — they add no signal.
110
148
  - Don't rewrite description prose beyond the targeted stale edits.
111
149
  - Don't modify the PR title.
112
150
  - Don't toggle draft state, close/reopen the PR, or post a separate PR
113
151
  comment narrating the description change.
114
- - Don't fail the work item on a screenshot or attachment failure — degrade
115
- gracefully and record the skip.
152
+ - Don't fail the work item on a screenshot or upload/attachment failure —
153
+ degrade gracefully and record the skip.
116
154
  - Screenshots **never** land in the repo. Save raw PNGs only under
117
155
  `agents/<id>/screenshots/`. No `git add` of any PNG, ever.
156
+ - Never run `gh auth switch`; build a per-command `GH_TOKEN` for the repo
157
+ owner's account.
118
158
 
119
159
  ## Completion-report shape
120
160
 
@@ -136,6 +176,13 @@ this block in the JSON completion report (see
136
176
  }
137
177
  ```
138
178
 
179
+ When you **introduce** screenshots for a visual change, set
180
+ `result: "screenshots-introduced"` (or
181
+ `"description-patched+screenshots-introduced"` when you also patched prose),
182
+ and list the embedded asset URLs in `newScreenshots`. Use
183
+ `"screenshots-refreshed"` when you only swapped pre-existing image refs, and a
184
+ `"screenshots-skipped (<reason>)"` shape when capture/upload was skipped.
185
+
139
186
  Omit `meta.descriptionAudit` entirely when the audit did not run (no
140
187
  commits pushed, no-op completion, etc.). When `meta.skipDescriptionAudit`
141
188
  suppressed the audit, set `ran: false` and
@@ -135,12 +135,12 @@ After you push commits to the PR's source branch and BEFORE you mark the work it
135
135
 
136
136
  **Out-of-scope guardrails**
137
137
 
138
- - Don't add screenshots that weren't already there. Refresh only.
138
+ - Only screenshot **meaningful visual/UI changes** (dashboard pages, slim-ux, Settings, work-item/PR/plan views, badges, layout/CSS); skip backend/engine/text/analysis/docs changes. When a build fix does touch a visual surface, proactively capture + embed via the release-asset recipe in `_pr-description-audit.md` (BEFORE/AFTER for fixes, AFTER-only for new UI).
139
139
  - Don't rewrite description prose beyond the targeted stale edits.
140
140
  - Don't modify the PR title; don't toggle draft state, close/reopen the PR.
141
141
  - Don't post a separate PR comment summarizing the description change.
142
- - Don't fail the work item on a screenshot or attachment failure — degrade gracefully and record the skip.
143
- - Screenshots NEVER land in the repo. `agents/<id>/screenshots/` only; never `git add` a PNG.
142
+ - Don't fail the work item on a screenshot or upload/attachment failure — degrade gracefully and record the skip.
143
+ - Screenshots NEVER land in the repo. `agents/<id>/screenshots/` only; never `git add` a PNG. Never `gh auth switch` — use a per-command `GH_TOKEN`.
144
144
 
145
145
  **Completion-report shape**
146
146
 
package/playbooks/docs.md CHANGED
@@ -111,8 +111,8 @@ git push -u origin {{branch_name}}
111
111
  ```
112
112
 
113
113
  Create the PR for docs tasks — docs go through the same review flow as code.
114
- Use the appropriate repo-host tooling for PR creation. For Azure DevOps, prefer the
115
- `az` CLI first and use the ADO MCP only as a fallback.
114
+
115
+ {{pr_create_instructions}}
116
116
  {{/shared_branch}}
117
117
 
118
118
  ## Rules
package/playbooks/fix.md CHANGED
@@ -47,13 +47,13 @@ This dispatch addresses finding `{{review_finding_id}}`. A successful review fix
47
47
  {
48
48
  "reviewFindingResolution": {
49
49
  "findingId": "{{review_finding_id}}",
50
- "disposition": "already-resolved",
50
+ "disposition": "satisfied-elsewhere",
51
51
  "currentCodeEvidence": "path/to/file.js:123 and the specific test or behavior that proves the finding is resolved"
52
52
  }
53
53
  }
54
54
  ```
55
55
 
56
- `disposition` may be `already-resolved` or `invalid`. Cite a current `file:line` or commit plus the inspected behavior; shared-account ownership, `viewerDidAuthor`, or reviewer/fixer equality alone is rejected and retried without consuming the PR no-op pause budget.
56
+ `disposition` may be `satisfied-elsewhere` or `invalid`; legacy `already-resolved` is accepted and canonicalized to `satisfied-elsewhere`. Cite a current `file:line` or commit plus the inspected behavior; shared-account ownership, `viewerDidAuthor`, or reviewer/fixer equality alone is rejected and retried without consuming the PR no-op pause budget.
57
57
  {{/review_finding_id}}
58
58
 
59
59
  For each review finding, reproduce or inspect the claimed issue before editing:
@@ -168,30 +168,30 @@ After you push commits to the PR's source branch and BEFORE you mark the work it
168
168
  - Azure DevOps: `az repos pr update --id <num> --description "$(Get-Content -Raw <file>)"`. If the body exceeds ~4 KB, fall back to `az rest --method patch` against `…/pullRequests/<id>?api-version=7.1` with `{ "description": "<body>" }`.
169
169
  7. GET-verify the description post-patch by re-running step 1 and confirming the body matches what you sent.
170
170
 
171
- **Screenshot handling (refresh-only never introduce screenshots)**
171
+ **Screenshot handling (introduce AND refresh for visual changes)**
172
172
 
173
- This applies only when the **existing** description embeds markdown image refs (`![alt](url)`). NEVER add screenshots to a PR that didn't already have any.
173
+ For a PR that contains a **meaningful visual/UI change**, proactively CAPTURE screenshots and EMBED them directly in the PR description do NOT rely only on the OPG `Visual evidence capture` CI bot (it stays as-is; this is additive PR-body evidence). When the description already embeds `![alt](url)` refs for a view this dispatch touched, refresh them the same way.
174
174
 
175
- 1. Extract every `![alt](url)` from the current description.
176
- 2. Decide which images depict views this dispatch's diff touched (heuristic: alt text / filename / surrounding prose mentions a component / route / view name present in the changed files).
177
- 3. If the view was touched AND the project has a runnable dev server (detect via `package.json` scripts named `dev`, `start`, or `serve`) AND Playwright MCP is available, recapture:
178
- - Spin up the dev server with a detached handoff per `shared-rules.md` "Long-Running Commands". Record PID + log path + URL + stop command.
179
- - Drive Playwright MCP to the relevant route and screenshot the view. Save raw PNGs to `agents/<id>/screenshots/` ONLY. **NEVER `git add` a PNG** — committing a screenshot is a release-blocker bug.
180
- - Upload as a PR attachment:
181
- - **ADO**: `PUT /pullRequests/<id>/attachments/<filename>` with the PNG bytes, then reference the returned URL in the description.
182
- - **GitHub**: no documented REST endpoint exists for PR-comment image uploads (the browser drag-drop uses `github.com/upload/asset`, which `gh` does not wrap). If you cannot find a reliable scripted upload path, **skip** the recapture, leave the old image URL in place, and record `meta.descriptionAudit.result = "screenshots-refreshed-skipped (gh-attachment-unavailable)"`.
183
- - Patch the description to swap old image URLs for new.
184
- - Stop the dev server using the recorded stop command.
185
- 4. If the dev server fails to start, the route 404s, Playwright fails, or the host's attachment API is unreachable **skip that screenshot** without failing the work item. Record the skip and the reason in `meta.descriptionAudit`.
175
+ **Scope guard — which changes get screenshots.** Only meaningful visual/UI changes: dashboard pages, slim-ux, Settings, work-item/PR/plan views, badges, layout/CSS. Skip backend-only, engine-logic, text/prose-only, analysis, and docs changes — those get NO screenshots (they add no signal). BEFORE/AFTER pair for layout/visual FIXES; AFTER-only for NEW UI.
176
+
177
+ 1. If the project has a runnable dev server (detect via `package.json` scripts named `dev`, `start`, or `serve`) AND Playwright MCP is available, spin it up with a detached handoff per `shared-rules.md` → "Long-Running Commands" (record PID + log path + URL + stop command).
178
+ 2. Drive Playwright MCP to the changed route at a normal viewport and screenshot the section that changed. Save raw PNGs to `agents/<id>/screenshots/` ONLY. **NEVER `git add` a PNG** — committing a screenshot is a release-blocker bug. Stop the dev server with the recorded stop command when done.
179
+ 3. Upload + embed:
180
+ - **GitHub (release-asset CDN recipe):** publish the PNGs as assets on a per-PR prerelease and embed the asset CDN URLs (works on PRIVATE repos, never commits PNGs). Build a per-command `GH_TOKEN` for the repo owner's account (`opg-microsoft` → `yemishin_microsoft`, `yemi33` → `yemi33`); **NEVER run `gh auth switch`**.
181
+ - bash: `GH_TOKEN=$(gh auth token --user yemishin_microsoft --hostname github.com)` then `GH_TOKEN=$GH_TOKEN gh release create "pr-${PR}-visual" agents/<id>/screenshots/*.png --repo <owner>/<repo> --title "PR #${PR} visual evidence" --prerelease --notes "..."` (on re-dispatch use `gh release upload "pr-${PR}-visual" … --clobber`). Resolve URLs via `gh api repos/<owner>/<repo>/releases/tags/pr-${PR}-visual --jq '.assets[]|select(.name=="after.png")|.browser_download_url'`.
182
+ - PowerShell: `$env:GH_TOKEN = (gh auth token --user yemishin_microsoft --hostname github.com); $pngs = Get-ChildItem agents/<id>/screenshots/*.png; gh release create "pr-$PR-visual" $pngs --repo <owner>/<repo> --title "PR #$PR visual evidence" --prerelease --notes "..."; if ($LASTEXITCODE -ne 0) { gh release upload "pr-$PR-visual" $pngs --repo <owner>/<repo> --clobber }` then `gh api ... --jq '...browser_download_url'`.
183
+ - Embed as `![<view> (after)](<url>)` and patch the body with `gh pr edit <num> --repo <owner>/<repo> --body-file <file>`.
184
+ - **ADO:** `PUT /pullRequests/<id>/attachments/<filename>` with the PNG bytes, then reference the returned URL in the description (now for newly-introduced screenshots too).
185
+ 4. Degrade gracefully: if the dev server won't start, the route 404s, Playwright is unavailable, or the upload failsSKIP without failing the work item and record the skip + reason in `meta.descriptionAudit` (e.g. `result: "screenshots-skipped (dev-server-unavailable)"`).
186
186
 
187
187
  **Out-of-scope guardrails**
188
188
 
189
- - Don't add screenshots that weren't already there. Refresh only.
189
+ - Only screenshot meaningful visual/UI changes (see scope guard); skip backend/engine/text/analysis/docs changes.
190
190
  - Don't rewrite description prose beyond the targeted stale edits.
191
191
  - Don't modify the PR title; don't toggle draft state, close/reopen the PR.
192
192
  - Don't post a separate PR comment summarizing the description change.
193
- - Don't fail the work item on a screenshot or attachment failure — degrade gracefully and record the skip.
194
- - Screenshots NEVER land in the repo. `agents/<id>/screenshots/` only; never `git add` a PNG.
193
+ - Don't fail the work item on a screenshot or upload/attachment failure — degrade gracefully and record the skip.
194
+ - Screenshots NEVER land in the repo. `agents/<id>/screenshots/` only; never `git add` a PNG. Never `gh auth switch` — use a per-command `GH_TOKEN`.
195
195
 
196
196
  **Completion-report shape**
197
197
 
@@ -140,30 +140,30 @@ After you push commits to the PR's source branch and BEFORE you mark the work it
140
140
  - Azure DevOps: `az repos pr update --id <num> --description "$(Get-Content -Raw <file>)"`. If the body exceeds ~4 KB, fall back to `az rest --method patch` against `…/pullRequests/<id>?api-version=7.1` with `{ "description": "<body>" }`.
141
141
  7. GET-verify the description post-patch by re-running step 1 and confirming the body matches what you sent.
142
142
 
143
- **Screenshot handling (refresh-only never introduce screenshots)**
144
-
145
- This applies only when the **existing** description embeds markdown image refs (`![alt](url)`). NEVER add screenshots to a PR that didn't already have any.
146
-
147
- 1. Extract every `![alt](url)` from the current description.
148
- 2. Decide which images depict views this dispatch's diff touched (heuristic: alt text / filename / surrounding prose mentions a component / route / view name present in the changed files).
149
- 3. If the view was touched AND the project has a runnable dev server (detect via `package.json` scripts named `dev`, `start`, or `serve`) AND Playwright MCP is available, recapture:
150
- - Spin up the dev server with a detached handoff per `shared-rules.md` "Long-Running Commands". Record PID + log path + URL + stop command.
151
- - Drive Playwright MCP to the relevant route and screenshot the view. Save raw PNGs to `agents/<id>/screenshots/` ONLY. **NEVER `git add` a PNG** — committing a screenshot is a release-blocker bug.
152
- - Upload as a PR attachment:
153
- - **ADO**: `PUT /pullRequests/<id>/attachments/<filename>` with the PNG bytes, then reference the returned URL in the description.
154
- - **GitHub**: no documented REST endpoint exists for PR-comment image uploads (the browser drag-drop uses `github.com/upload/asset`, which `gh` does not wrap). If you cannot find a reliable scripted upload path, **skip** the recapture, leave the old image URL in place, and record `meta.descriptionAudit.result = "screenshots-refreshed-skipped (gh-attachment-unavailable)"`.
155
- - Patch the description to swap old image URLs for new.
156
- - Stop the dev server using the recorded stop command.
157
- 4. If the dev server fails to start, the route 404s, Playwright fails, or the host's attachment API is unreachable **skip that screenshot** without failing the work item. Record the skip and the reason in `meta.descriptionAudit`.
143
+ **Screenshot handling (introduce AND refresh for visual changes)**
144
+
145
+ For a PR that contains a **meaningful visual/UI change**, proactively CAPTURE screenshots and EMBED them directly in the PR description do NOT rely only on the OPG `Visual evidence capture` CI bot (it stays as-is; this is additive PR-body evidence). When the description already embeds `![alt](url)` refs for a view this dispatch touched, refresh them the same way.
146
+
147
+ **Scope guard — which changes get screenshots.** Only meaningful visual/UI changes: dashboard pages, slim-ux, Settings, work-item/PR/plan views, badges, layout/CSS. Skip backend-only, engine-logic, text/prose-only, analysis, and docs changes — those get NO screenshots (they add no signal). BEFORE/AFTER pair for layout/visual FIXES; AFTER-only for NEW UI.
148
+
149
+ 1. If the project has a runnable dev server (detect via `package.json` scripts named `dev`, `start`, or `serve`) AND Playwright MCP is available, spin it up with a detached handoff per `shared-rules.md` → "Long-Running Commands" (record PID + log path + URL + stop command).
150
+ 2. Drive Playwright MCP to the changed route at a normal viewport and screenshot the section that changed. Save raw PNGs to `agents/<id>/screenshots/` ONLY. **NEVER `git add` a PNG** — committing a screenshot is a release-blocker bug. Stop the dev server with the recorded stop command when done.
151
+ 3. Upload + embed:
152
+ - **GitHub (release-asset CDN recipe):** publish the PNGs as assets on a per-PR prerelease and embed the asset CDN URLs (works on PRIVATE repos, never commits PNGs). Build a per-command `GH_TOKEN` for the repo owner's account (`opg-microsoft` → `yemishin_microsoft`, `yemi33` → `yemi33`); **NEVER run `gh auth switch`**.
153
+ - bash: `GH_TOKEN=$(gh auth token --user yemishin_microsoft --hostname github.com)` then `GH_TOKEN=$GH_TOKEN gh release create "pr-${PR}-visual" agents/<id>/screenshots/*.png --repo <owner>/<repo> --title "PR #${PR} visual evidence" --prerelease --notes "..."` (on re-dispatch use `gh release upload "pr-${PR}-visual" … --clobber`). Resolve URLs via `gh api repos/<owner>/<repo>/releases/tags/pr-${PR}-visual --jq '.assets[]|select(.name=="after.png")|.browser_download_url'`.
154
+ - PowerShell: `$env:GH_TOKEN = (gh auth token --user yemishin_microsoft --hostname github.com); $pngs = Get-ChildItem agents/<id>/screenshots/*.png; gh release create "pr-$PR-visual" $pngs --repo <owner>/<repo> --title "PR #$PR visual evidence" --prerelease --notes "..."; if ($LASTEXITCODE -ne 0) { gh release upload "pr-$PR-visual" $pngs --repo <owner>/<repo> --clobber }` then `gh api ... --jq '...browser_download_url'`.
155
+ - Embed as `![<view> (after)](<url>)` and patch the body with `gh pr edit <num> --repo <owner>/<repo> --body-file <file>`.
156
+ - **ADO:** `PUT /pullRequests/<id>/attachments/<filename>` with the PNG bytes, then reference the returned URL in the description (now for newly-introduced screenshots too).
157
+ 4. Degrade gracefully: if the dev server won't start, the route 404s, Playwright is unavailable, or the upload failsSKIP without failing the work item and record the skip + reason in `meta.descriptionAudit` (e.g. `result: "screenshots-skipped (dev-server-unavailable)"`).
158
158
 
159
159
  **Out-of-scope guardrails**
160
160
 
161
- - Don't add screenshots that weren't already there. Refresh only.
161
+ - Only screenshot meaningful visual/UI changes (see scope guard); skip backend/engine/text/analysis/docs changes.
162
162
  - Don't rewrite description prose beyond the targeted stale edits.
163
163
  - Don't modify the PR title; don't toggle draft state, close/reopen the PR.
164
164
  - Don't post a separate PR comment summarizing the description change.
165
- - Don't fail the work item on a screenshot or attachment failure — degrade gracefully and record the skip.
166
- - Screenshots NEVER land in the repo. `agents/<id>/screenshots/` only; never `git add` a PNG.
165
+ - Don't fail the work item on a screenshot or upload/attachment failure — degrade gracefully and record the skip.
166
+ - Screenshots NEVER land in the repo. `agents/<id>/screenshots/` only; never `git add` a PNG. Never `gh auth switch` — use a per-command `GH_TOKEN`.
167
167
 
168
168
  **Completion-report shape**
169
169
 
@@ -29,8 +29,9 @@ natural-language flows into a runner-native test file.
29
29
  - **Capture:** `{{capture}}`
30
30
  - **Mode:** `{{session_mode}}`
31
31
  - **Managed-spawn target:** `{{managed_spawn_name}}` (live — query
32
- `http://localhost:7331/api/managed-processes/by-name?name={{managed_spawn_name}}`
32
+ `{{managed_spawn_lookup_url}}`
33
33
  for the freshest port / base URL / health).
34
+ - **Test output directory (absolute):** `{{qa_test_dir}}`
34
35
 
35
36
  {{additional_context}}
36
37
 
@@ -41,8 +42,8 @@ engine dispatches for each QA Session (SETUP → DRAFT → EXECUTE). The SETUP
41
42
  agent already produced a managed-spawn sidecar and the engine spawned the
42
43
  dev-up command; the EXECUTE agent will run your drafted test against that
43
44
  live spawn. Your only deliverable is the **test file itself**, written in
44
- the runner's native format under
45
- `engine/qa-tests/{{session_id}}/` (relative to the Minions root).
45
+ the runner's native format under the injected absolute Minions-owned
46
+ directory `{{qa_test_dir}}`.
46
47
 
47
48
  The engine resolved a concrete **runner adapter** for this session
48
49
  (Playwright, Maestro, or a project plugin) and its `generateBrief()` hook
@@ -60,7 +61,7 @@ runner brief below, then implement exactly the file it describes.
60
61
  ### Reporting the test file path
61
62
 
62
63
  When you exit, your completion JSON MUST include a `testFile` field with
63
- the **relative path inside `engine/qa-tests/{{session_id}}/`** of the file
64
+ the **relative path inside `{{qa_test_dir}}`** of the file
64
65
  you wrote (e.g. `test.spec.js`, `flow.yaml`). The engine reads this and
65
66
  stores it on the session record so the EXECUTE prompt can reference it
66
67
  directly. Without `testFile`, EXECUTE falls back to a generic
@@ -87,9 +88,9 @@ Example:
87
88
  - commit, push, or open a pull request — sessions are tracked by the
88
89
  session record, not a merged PR
89
90
  - modify project source — the only file you should write is the test
90
- file under `engine/qa-tests/{{session_id}}/`
91
+ file under `{{qa_test_dir}}`
91
92
  - start the managed-spawn yourself — it is already running; query
92
- `/api/managed-processes/by-name?name={{managed_spawn_name}}` for the
93
+ `{{managed_spawn_lookup_url}}` for the
93
94
  live port / base URL / health snapshot
94
95
 
95
96
  ## Failure path (REQUIRED)
@@ -135,12 +136,9 @@ pwd
135
136
  ```
136
137
 
137
138
  `MINIONS_AGENT_CWD` is the engine-resolved worktree root. Prefer it over
138
- `pwd` for any cwd-sensitive command. The test file path is **relative to
139
- the Minions root**, not the project worktree write to
140
- `<MINIONS_ROOT>/engine/qa-tests/{{session_id}}/`. The Minions root is the
141
- parent of the project worktree (one level above `MINIONS_AGENT_CWD` for
142
- project-scoped sessions; equal to `MINIONS_AGENT_CWD` for central
143
- sessions).
139
+ `pwd` for any cwd-sensitive command. Do not derive the Minions root from
140
+ the project worktree. Write the test only through the injected absolute
141
+ directory `{{qa_test_dir}}`.
144
142
 
145
143
  ## Findings
146
144
 
@@ -21,7 +21,7 @@ ingests.
21
21
  - **Session id:** `{{session_id}}`
22
22
  - **Session phase:** `{{session_phase}}`
23
23
  - **Managed-spawn target:** `{{managed_spawn_name}}` (live — query
24
- `http://localhost:7331/api/managed-processes/by-name?name={{managed_spawn_name}}`
24
+ `{{managed_spawn_lookup_url}}`
25
25
  for the freshest port / base URL / health).
26
26
  - **Test file (relative to `engine/qa-tests/{{session_id}}/`):** `{{test_file}}`
27
27
  - **Flows (for context):** {{flows_raw}}
@@ -46,8 +46,11 @@ below.
46
46
 
47
47
  ### Result sidecar (REQUIRED)
48
48
 
49
- Before exit, write the result sidecar at
50
- `agents/{{agent_id}}/qa-run-result.json` with this exact shape:
49
+ Store every capture under the injected absolute linked-run directory
50
+ `{{qa_artifacts_dir}}`. Before exit, write the result sidecar at the injected
51
+ absolute path `{{qa_result_sidecar}}`. Artifact `path` values are relative to
52
+ `{{qa_artifacts_dir}}`; preserve nested runner output directories. Use this exact
53
+ shape:
51
54
 
52
55
  ```json
53
56
  {
@@ -57,7 +60,7 @@ Before exit, write the result sidecar at
57
60
  "artifacts": [
58
61
  {
59
62
  "type": "screenshot",
60
- "path": "engine/qa-artifacts/{{session_id}}/01-login-form.png",
63
+ "path": "test-capture-layout/01-login-form.png",
61
64
  "label": "Login form rendered",
62
65
  "capturedAt": "2026-05-20T20:42:00.000Z"
63
66
  }
@@ -114,9 +117,10 @@ green**. Write:
114
117
  }
115
118
  ```
116
119
 
117
- …AND write a matching `qa-run-result.json` sidecar with `status: "errored"`
118
- so the qa-runs record terminalizes correctly. The session will transition
119
- to `failed` with `failureClass: qa-session-execute-failed`.
120
+ …AND write a matching `qa-run-result.json` sidecar to
121
+ `{{qa_result_sidecar}}` with `status: "errored"` so the qa-runs record
122
+ terminalizes correctly. The session will transition to `failed` with
123
+ `failureClass: qa-session-execute-failed`.
120
124
 
121
125
  ## Working directory
122
126
 
@@ -131,10 +135,10 @@ pwd
131
135
  ```
132
136
 
133
137
  `MINIONS_AGENT_CWD` is the engine-resolved worktree root. Prefer it over
134
- `pwd` for any cwd-sensitive command. The test file path is **relative to
135
- the Minions root**: full path is
136
- `<MINIONS_ROOT>/engine/qa-tests/{{session_id}}/{{test_file}}`. Capture
137
- artifacts to `<MINIONS_ROOT>/engine/qa-artifacts/{{session_id}}/`.
138
+ `pwd` for any cwd-sensitive command. The runner execute brief above resolves
139
+ the drafted test file under the Minions root rather than this disposable
140
+ project worktree. Capture artifacts only under the injected absolute
141
+ `{{qa_artifacts_dir}}` directory, which is keyed by `{{qa_run_id}}`.
138
142
 
139
143
  ## Long-Running Commands
140
144
 
@@ -159,7 +163,7 @@ only after successful completion. Include:
159
163
  - Do not modify production code unless explicitly asked.
160
164
  - Do not remove worktrees; the engine handles cleanup automatically.
161
165
  - Do not start or restart the managed-spawn — the engine owns it.
162
- - Always emit the `qa-run-result.json` sidecar before exit — even a
166
+ - Always emit `{{qa_result_sidecar}}` before exit — even a
163
167
  single-field
164
168
  `{"runId": "{{qa_run_id}}", "status": "errored", "summary": "...", "artifacts": []}`
165
169
  is better than an absent file.
@@ -36,12 +36,13 @@ block above) and registered a `qaRunId`. Your job:
36
36
  3. Execute each step in order. Use Playwright, `curl`, `Invoke-WebRequest`, or
37
37
  manual instructions as appropriate for the step's `command` field (if
38
38
  present) or `description`.
39
- 4. Save every artifact you produce as a file under
40
- `{{qa_artifacts_dir}}` exactly the path you will reference in the
41
- sidecar. Use one of the documented types: `screenshot`, `video`, `log`,
42
- `other`.
43
- 5. Before exit, write the result sidecar at
44
- `agents/{{agent_id}}/qa-run-result.json` with this exact shape:
39
+ 4. Save every artifact you produce as a file under the injected absolute
40
+ directory `{{qa_artifacts_dir}}`. In the sidecar, record the corresponding
41
+ Minions-root-relative `engine/qa-artifacts/{{qa_run_id}}/<file>` path shown
42
+ below so API responses do not expose host filesystem paths. Use one of the
43
+ documented types: `screenshot`, `video`, `log`, `other`.
44
+ 5. Before exit, write the result sidecar at the injected absolute path
45
+ `{{qa_result_sidecar}}` with this exact shape:
45
46
 
46
47
  ```json
47
48
  {
@@ -51,7 +52,7 @@ block above) and registered a `qaRunId`. Your job:
51
52
  "artifacts": [
52
53
  {
53
54
  "type": "screenshot",
54
- "path": "{{qa_artifacts_dir}}/01-login-form.png",
55
+ "path": "engine/qa-artifacts/{{qa_run_id}}/01-login-form.png",
55
56
  "label": "Login form rendered",
56
57
  "capturedAt": "2026-05-20T20:42:00.000Z"
57
58
  }
@@ -118,6 +118,51 @@ The engine provides a completion report path in the prompt and in `MINIONS_COMPL
118
118
 
119
119
  **Trust nonce (REQUIRED).** The engine injects a per-spawn cryptographic value into your environment as `MINIONS_COMPLETION_NONCE`. Copy it verbatim into the report's `nonce` field — do not invent, regenerate, or share it with other dispatches. The engine validates the nonce on read; mismatched or (when strict) missing nonces are treated as forged and the dispatch is failed with `failure_class: 'completion-nonce-mismatch'`. See `docs/completion-reports.md` → "Trust boundary" for details.
120
120
 
121
+ ### Review-learning evidence
122
+
123
+ For a review or fix dispatch that supplies trusted finding or comment source
124
+ identities, you may include at most eight `reviewLearnings` and at most 32
125
+ `reviewLearningApplications` in the completion report:
126
+
127
+ ```json
128
+ {
129
+ "reviewLearnings": [{
130
+ "rule": "State one generalized procedural rule.",
131
+ "disposition": "accepted-fixed",
132
+ "source": {
133
+ "pr": "<canonical dispatch PR id>",
134
+ "sourceKey": "<trusted finding or comment source key from the dispatch>"
135
+ },
136
+ "project": "<dispatch project>",
137
+ "authorAgent": "<tracked PR author agent>",
138
+ "applicability": "project-team",
139
+ "files": ["path/to/file.js"],
140
+ "symbols": ["symbolName"],
141
+ "tags": ["review-learning"],
142
+ "evidence": ["path/to/file.js:123 or commit abc1234"],
143
+ "confidence": 0.9
144
+ }],
145
+ "reviewLearningApplications": [{
146
+ "id": "M-lesson-id",
147
+ "outcome": "considered-not-applicable",
148
+ "evidence": "The task did not touch the lesson's file or symbol scope."
149
+ }]
150
+ }
151
+ ```
152
+
153
+ Application outcomes are `applied`, `considered-not-applicable`, or
154
+ `contradicted`. Use only the exact trusted source identity supplied with the
155
+ dispatch. Report generalized rules, outcome labels, and observable evidence
156
+ references only. Never include reasoning transcripts or chain-of-thought.
157
+ Malformed optional learning data is diagnosed and dropped without changing an
158
+ otherwise successful fix result. A nonce mismatch or
159
+ `securityFlags.injectionAttempt: true` suppresses both payloads.
160
+
161
+ An unchanged review-feedback fix must still satisfy
162
+ `reviewFindingResolution`; a `reviewLearnings` entry is not no-op proof. See
163
+ `docs/completion-reports.md` for field types, canonical dispositions, byte
164
+ limits, and the optional `repeatedOf` ID.
165
+
121
166
  For the canonical schema — every field, the `failure_class` enum, `noop:true` semantics, `retryable` / `needs_rerun` shape, and the artifacts array — see `docs/completion-reports.md`. The JSON report is the primary signal; fenced `completion` blocks in stdout are accepted only as a fallback.
122
167
 
123
168
  ## Minions API access
package/playbooks/test.md CHANGED
@@ -36,6 +36,10 @@ This task is part of a **shared-branch plan** — other plan items share `{{bran
36
36
  {{/shared_branch}}
37
37
  {{^shared_branch}}
38
38
  If the task asks you to add or modify files, commit those focused changes, push the branch, and create a PR using the same conventions as implement tasks. For pure build/run/verify tasks, do not push or create a PR.
39
+
40
+ When you do create a PR:
41
+
42
+ {{pr_create_instructions}}
39
43
  {{/shared_branch}}
40
44
 
41
45
  If a build or test fails, report the error clearly instead of fixing it unless the task explicitly asks for a fix.
@@ -347,7 +347,7 @@ The `X-CC-Turn-Id` header is the audit trail — the handler stamps it onto the
347
347
 
348
348
  **Multi-action turns:** make multiple sequential `curl` calls. Each successful state-changing call yields its own chip in the user's reply.
349
349
 
350
- **Errors:** if a `curl` returns 4xx/5xx, surface the error text in your reply so the user sees what went wrong. Don't retry blindlyusually the body explains the missing field.
350
+ **Errors:** the API returns a stable envelope `{ error, code, field?, expected?, allowedValues?, ... }` (4xx) or a generic `internal-error` (5xx). Before crafting a request, prefer discovering a route's accepted inputs and constraints from `GET /api/routes` (each entry publishes its `request` policy/fields, `aliases`, and `negativeTests`) rather than guessing. On a **4xx** (400/404/409/422 — your payload is wrong), self-correct from the structured body read `code`/`field`/`expected`/`allowedValues`, fix that field, and resend once; surface the message to the user. On a **5xx / 503** a dependency or the engine failed, not your request do NOT blindly resend in a loop; surface it and, if anything, retry with backoff. See `docs/api-errors.md` for the full contract.
351
351
 
352
352
  ## Project checkout modes (worktree / live / hybrid)
353
353
 
@@ -21,13 +21,13 @@ All Minions agents may post through one GitHub or ADO credential. `viewerDidAuth
21
21
  {
22
22
  "reviewFindingResolution": {
23
23
  "findingId": "<review_finding_id>",
24
- "disposition": "already-resolved",
24
+ "disposition": "satisfied-elsewhere",
25
25
  "currentCodeEvidence": "path/to/file.js:123 and the test or behavior proving the disposition"
26
26
  }
27
27
  }
28
28
  ```
29
29
 
30
- 5. Use `disposition: "invalid"` only when current-code evidence disproves the claim. A platform-identity observation is not current-code evidence.
30
+ 5. Use `disposition: "invalid"` only when current-code evidence disproves the claim. Legacy `already-resolved` remains accepted and is canonicalized to `satisfied-elsewhere`. A platform-identity observation is not current-code evidence.
31
31
 
32
32
  ## Duplicate findings
33
33