@iceinvein/agent-skills 0.1.39 → 0.2.0

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 (148) hide show
  1. package/README.md +18 -2
  2. package/dist/cli/index.js +105 -28
  3. package/package.json +1 -1
  4. package/skills/index.json +14 -2
  5. package/skills/magpie/README.md +2 -1
  6. package/skills/magpie/SKILL.md +138 -45
  7. package/skills/magpie/bin/magpie.ts +43 -0
  8. package/skills/magpie/fixtures/example-pr/brief.json +18 -0
  9. package/skills/magpie/fixtures/fake-gh-nodiff.sh +38 -0
  10. package/skills/magpie/fixtures/fake-gh.sh +14 -0
  11. package/skills/magpie/package.json +1 -1
  12. package/skills/magpie/references/peer-review.md +7 -2
  13. package/skills/magpie/references/scout.md +90 -0
  14. package/skills/magpie/references/specialists.md +117 -6
  15. package/skills/magpie/scripts/__tests__/cli.test.ts +101 -1
  16. package/skills/magpie/scripts/__tests__/dedupe-cmd.test.ts +187 -0
  17. package/skills/magpie/scripts/__tests__/diff-chunks.test.ts +51 -0
  18. package/skills/magpie/scripts/__tests__/filter-diff-preservation.test.ts +54 -0
  19. package/skills/magpie/scripts/__tests__/findings-files.test.ts +35 -0
  20. package/skills/magpie/scripts/__tests__/gh.test.ts +90 -0
  21. package/skills/magpie/scripts/__tests__/git-diff.test.ts +83 -0
  22. package/skills/magpie/scripts/__tests__/helpers/git-fixture.ts +47 -0
  23. package/skills/magpie/scripts/__tests__/path-filter.test.ts +27 -0
  24. package/skills/magpie/scripts/__tests__/preview-cmd.test.ts +16 -0
  25. package/skills/magpie/scripts/__tests__/refresh.test.ts +45 -0
  26. package/skills/magpie/scripts/__tests__/render-cmd.test.ts +173 -1
  27. package/skills/magpie/scripts/__tests__/render-findings.test.ts +151 -1
  28. package/skills/magpie/scripts/__tests__/render-progress.test.ts +42 -0
  29. package/skills/magpie/scripts/__tests__/setup-cmd.test.ts +83 -1
  30. package/skills/magpie/scripts/__tests__/shard.test.ts +165 -0
  31. package/skills/magpie/scripts/__tests__/skill-lint.test.ts +207 -7
  32. package/skills/magpie/scripts/__tests__/types.test.ts +47 -0
  33. package/skills/magpie/scripts/dedupe-cmd.ts +58 -3
  34. package/skills/magpie/scripts/diff-chunks.ts +28 -0
  35. package/skills/magpie/scripts/findings-files.ts +32 -0
  36. package/skills/magpie/scripts/gh.ts +68 -14
  37. package/skills/magpie/scripts/git-diff.ts +111 -0
  38. package/skills/magpie/scripts/path-filter.ts +9 -5
  39. package/skills/magpie/scripts/preview-cmd.ts +11 -1
  40. package/skills/magpie/scripts/refresh.ts +32 -3
  41. package/skills/magpie/scripts/render-cmd.ts +35 -11
  42. package/skills/magpie/scripts/render-findings.ts +78 -2
  43. package/skills/magpie/scripts/render-progress.ts +6 -1
  44. package/skills/magpie/scripts/setup-cmd.ts +38 -1
  45. package/skills/magpie/scripts/shard.ts +171 -0
  46. package/skills/magpie/scripts/status-cmd.ts +7 -4
  47. package/skills/magpie/scripts/types.ts +50 -0
  48. package/skills/magpie/skill.json +2 -2
  49. package/skills/magpie/templates/styles.css +66 -0
  50. package/skills/migrate/README.md +194 -0
  51. package/skills/migrate/SKILL.md +197 -0
  52. package/skills/migrate/bin/migrate +15 -0
  53. package/skills/migrate/bin/migrate.ts +309 -0
  54. package/skills/migrate/biome.json +35 -0
  55. package/skills/migrate/bun.lock +24 -0
  56. package/skills/migrate/docs/architecture.md +294 -0
  57. package/skills/migrate/docs/reference.md +590 -0
  58. package/skills/migrate/fixtures/tiny-express/GROUND-TRUTH.md +39 -0
  59. package/skills/migrate/fixtures/tiny-express/app.js +29 -0
  60. package/skills/migrate/fixtures/tiny-express/cron.js +6 -0
  61. package/skills/migrate/fixtures/tiny-express/reports/daily-users.json +6 -0
  62. package/skills/migrate/fixtures/tiny-express/schema.sql +12 -0
  63. package/skills/migrate/fixtures/tiny-express/settings.json +4 -0
  64. package/skills/migrate/fixtures/tiny-express/views/users.html +9 -0
  65. package/skills/migrate/fixtures/tiny-webforms/Controllers/UsersController.cs +68 -0
  66. package/skills/migrate/fixtures/tiny-webforms/Default.aspx +7 -0
  67. package/skills/migrate/fixtures/tiny-webforms/Default.aspx.cs +14 -0
  68. package/skills/migrate/fixtures/tiny-webforms/GROUND-TRUTH.md +50 -0
  69. package/skills/migrate/fixtures/tiny-webforms/Integrations/BillingClient.cs +16 -0
  70. package/skills/migrate/fixtures/tiny-webforms/Jobs/NightlyDigestJob.cs +33 -0
  71. package/skills/migrate/fixtures/tiny-webforms/Reports/DailyUsers.rdl +11 -0
  72. package/skills/migrate/fixtures/tiny-webforms/Schema.sql +12 -0
  73. package/skills/migrate/fixtures/tiny-webforms/Site.master +16 -0
  74. package/skills/migrate/fixtures/tiny-webforms/Users.aspx +8 -0
  75. package/skills/migrate/fixtures/tiny-webforms/Users.aspx.cs +14 -0
  76. package/skills/migrate/fixtures/tiny-webforms/web.config +10 -0
  77. package/skills/migrate/install.sh +68 -0
  78. package/skills/migrate/package.json +17 -0
  79. package/skills/migrate/references/phases/enumerate.md +291 -0
  80. package/skills/migrate/references/phases/extract.md +652 -0
  81. package/skills/migrate/references/phases/parity.md +275 -0
  82. package/skills/migrate/references/phases/probe.md +135 -0
  83. package/skills/migrate/references/phases/queue.md +242 -0
  84. package/skills/migrate/references/phases/seam.md +416 -0
  85. package/skills/migrate/references/recipes/README.md +116 -0
  86. package/skills/migrate/references/recipes/aspnet.md +287 -0
  87. package/skills/migrate/references/run-ops.md +280 -0
  88. package/skills/migrate/scripts/__tests__/census.test.ts +775 -0
  89. package/skills/migrate/scripts/__tests__/check.test.ts +458 -0
  90. package/skills/migrate/scripts/__tests__/citations.test.ts +156 -0
  91. package/skills/migrate/scripts/__tests__/cli.test.ts +183 -0
  92. package/skills/migrate/scripts/__tests__/concurrency.test.ts +164 -0
  93. package/skills/migrate/scripts/__tests__/config.test.ts +112 -0
  94. package/skills/migrate/scripts/__tests__/e2e-express.test.ts +1093 -0
  95. package/skills/migrate/scripts/__tests__/e2e-webforms.test.ts +1276 -0
  96. package/skills/migrate/scripts/__tests__/e2e.test.ts +320 -0
  97. package/skills/migrate/scripts/__tests__/ids.test.ts +38 -0
  98. package/skills/migrate/scripts/__tests__/import.test.ts +155 -0
  99. package/skills/migrate/scripts/__tests__/init.test.ts +192 -0
  100. package/skills/migrate/scripts/__tests__/leaks.test.ts +176 -0
  101. package/skills/migrate/scripts/__tests__/lock.test.ts +183 -0
  102. package/skills/migrate/scripts/__tests__/paths.test.ts +129 -0
  103. package/skills/migrate/scripts/__tests__/phase-cmd.test.ts +151 -0
  104. package/skills/migrate/scripts/__tests__/phases.test.ts +70 -0
  105. package/skills/migrate/scripts/__tests__/queue.test.ts +475 -0
  106. package/skills/migrate/scripts/__tests__/report.test.ts +150 -0
  107. package/skills/migrate/scripts/__tests__/run-state.test.ts +136 -0
  108. package/skills/migrate/scripts/__tests__/status-reset.test.ts +318 -0
  109. package/skills/migrate/scripts/__tests__/store.test.ts +132 -0
  110. package/skills/migrate/scripts/__tests__/validate.test.ts +54 -0
  111. package/skills/migrate/scripts/census-cmd.ts +109 -0
  112. package/skills/migrate/scripts/census.ts +342 -0
  113. package/skills/migrate/scripts/check-cmd.ts +24 -0
  114. package/skills/migrate/scripts/check.ts +376 -0
  115. package/skills/migrate/scripts/citations.ts +92 -0
  116. package/skills/migrate/scripts/config.ts +237 -0
  117. package/skills/migrate/scripts/ids.ts +31 -0
  118. package/skills/migrate/scripts/import-cmd.ts +141 -0
  119. package/skills/migrate/scripts/init-cmd.ts +118 -0
  120. package/skills/migrate/scripts/leaks.ts +184 -0
  121. package/skills/migrate/scripts/lock.ts +188 -0
  122. package/skills/migrate/scripts/paths.ts +103 -0
  123. package/skills/migrate/scripts/phase-cmd.ts +63 -0
  124. package/skills/migrate/scripts/phases.ts +113 -0
  125. package/skills/migrate/scripts/queue-cmd.ts +98 -0
  126. package/skills/migrate/scripts/queue.ts +258 -0
  127. package/skills/migrate/scripts/report-cmd.ts +47 -0
  128. package/skills/migrate/scripts/report.ts +131 -0
  129. package/skills/migrate/scripts/reset-cmd.ts +120 -0
  130. package/skills/migrate/scripts/status-cmd.ts +52 -0
  131. package/skills/migrate/scripts/store.ts +159 -0
  132. package/skills/migrate/scripts/types.ts +137 -0
  133. package/skills/migrate/scripts/validate.ts +221 -0
  134. package/skills/migrate/skill.json +33 -0
  135. package/skills/migrate/templates/config.toml +27 -0
  136. package/skills/migrate/templates/queue-item.md +17 -0
  137. package/skills/migrate/tsconfig.json +18 -0
  138. package/skills/migrate/uninstall.sh +31 -0
  139. package/skills/sluice/SKILL.md +82 -0
  140. package/skills/sluice/references/deep-channel.md +94 -0
  141. package/skills/sluice/references/finish.md +35 -0
  142. package/skills/sluice/references/intent.md +29 -0
  143. package/skills/sluice/references/review.md +42 -0
  144. package/skills/sluice/references/root-cause.md +38 -0
  145. package/skills/sluice/references/show-or-say.md +36 -0
  146. package/skills/sluice/references/test-first.md +35 -0
  147. package/skills/sluice/references/verify.md +26 -0
  148. package/skills/sluice/skill.json +32 -0
@@ -7,7 +7,7 @@ description: Use when the user asks to review a GitHub pull request (a PR number
7
7
 
8
8
  ## Prerequisites
9
9
 
10
- The skill pre-flights `bun`, `gh`, `git` (required) and `codex` (optional). If a required binary is missing the run aborts with a single install hint line. `codex` is the preferred peer reviewer, but it is optional: if it is missing the run continues and the peer-review stage falls back to a Claude second-opinion subagent (setup prints a one-line notice and logs `{stage: preflight, status: done, missingOptional: ["codex"]}`).
10
+ The skill pre-flights `bun`, `gh`, `git` (required) and `codex` (optional). A missing required binary aborts the run with a single install hint line. Without `codex` the run continues and peer review falls back to a Claude second-opinion subagent (setup prints a one-line notice and logs `{stage: preflight, status: done, missingOptional: ["codex"]}`).
11
11
 
12
12
  ## Stage walkthrough
13
13
 
@@ -15,15 +15,15 @@ Stop reading and follow these steps in order. Do not skip stages. Use the exact
15
15
 
16
16
  ### 0. Identify the PR
17
17
 
18
- Parse the user's request for a PR number, URL, or "this PR" (current branch). If ambiguous, ask one clarifying terminal question. Capture the PR number into `$PR_NUMBER` and the repository path into `$REPO` (default: current working directory).
18
+ Parse the user's request for a PR number, URL, or "this PR" (current branch). If ambiguous, ask one clarifying terminal question. Capture the PR number into `$PR_NUMBER` and the repo path into `$REPO` (default: the current working directory).
19
19
 
20
- Then check whether an earlier run on this PR is still unfinished, before minting a new id:
20
+ Before minting a new id, check whether an earlier run on this PR is unfinished:
21
21
 
22
22
  ```
23
23
  magpie --list-runs
24
24
  ```
25
25
 
26
- Each line is `<id>\t<active|archived>\t<path>`. If an `active` id matches `pr-${PR_NUMBER}-*`, that run was interrupted rather than cleaned up. Set `RUN_DIR` to its path and go to "Resuming a crashed run" instead of starting over; ask the user first if it is unclear whether they want to resume or review from scratch. (`archived` ids are finished runs, not resumable.)
26
+ Each line is `<id>\t<active|archived>\t<path>`. If an `active` id matches `pr-${PR_NUMBER}-*`, that run was interrupted rather than cleaned up. Set `RUN_DIR` to its path and go to "Resuming a crashed run"; ask the user first if it is unclear whether they want to resume or start over. (`archived` ids are finished runs, not resumable.)
27
27
 
28
28
  Otherwise compute a fresh run directory:
29
29
 
@@ -38,15 +38,30 @@ RUN_DIR="$HOME/.magpie/$RUN_ID"
38
38
  magpie setup "$RUN_DIR" --pr $PR_NUMBER --repo "$REPO"
39
39
  ```
40
40
 
41
- If exit is non-zero, surface stderr verbatim and stop. The CLI removes the worktree and subdirs on failure; the run directory itself plus `log.jsonl` are kept for diagnostics.
41
+ If exit is non-zero, surface stderr verbatim and stop. The CLI removes the worktree and subdirs on failure, keeping the run directory and `log.jsonl` for diagnostics.
42
42
 
43
- Setup automatically filters lockfiles, build output, generated source, and snapshot fixtures from `diff.patch` before specialists see it. Users can override by placing `.magpie.json` at the repo root: `{"exclude": [...glob], "include": [...glob], "useDefaults": true|false}`. When anything is filtered, the raw diff is preserved as `$RUN_DIR/diff.full.patch` and the exclusion list as `$RUN_DIR/excluded-files.json`.
43
+ Setup filters lockfiles, build output, generated source, and snapshot fixtures from `diff.patch` before specialists see it. Users override with `.magpie.json` at the repo root: `{"exclude": [...glob], "include": [...glob], "useDefaults": true|false}`. When anything is filtered, the raw diff is kept as `$RUN_DIR/diff.full.patch` and the exclusion list as `$RUN_DIR/excluded-files.json`.
44
44
 
45
- When a prior run exists for the same PR (active or archived under `~/.magpie/`), setup writes `$RUN_DIR/incremental.json` with `{previousRunId, previousSha, currentSha, sameSha}`. The post stage surfaces this as a "Incremental review since `<sha>`" trailer on the summary comment.
45
+ When `gh pr diff` refuses the diff (HTTP 406 above roughly 300 files) or returns an
46
+ empty diff for a PR with changed files, setup rebuilds it from the local clone instead
47
+ of aborting: it fetches `pull/<n>/head` and diffs from the merge base against the PR's
48
+ base branch, reproducing the three-dot semantics `gh pr diff` uses. A local head that
49
+ does not match the PR's `headRefOid` is a hard error, not a silently stale review. The
50
+ `fetch-pr` log entry records `source: "gh" | "git"` and the merge base, and
51
+ `$RUN_DIR/diff-source.json` carries the same for the report.
52
+
53
+ Setup then splits the filtered diff into shards, writing `$RUN_DIR/shards/manifest.json`
54
+ and, when more than one shard results, `$RUN_DIR/shards/shard-<n>.patch`. `diff.patch`
55
+ itself is never modified: shards are views over it. Re-split with a different budget
56
+ using `magpie shard "$RUN_DIR" --budget <lines> --max-files <n>` (defaults: 6000 patch
57
+ lines, 80 files). Re-splitting invalidates every existing
58
+ `findings/<focus>.shard-<n>.json`, since a shard id then names a different file set:
59
+ delete those files first, or stage 4's resume rule counts a pair as covered that
60
+ nothing reviewed.
46
61
 
47
- Setup also runs a deterministic test-coverage check: when the diff contains zero test or spec files anywhere, each non-test source file with `>= 10` added code lines gets a `domain: "tests"` finding written to `$RUN_DIR/findings/tests.json`. This is a sixth domain that flows through dedupe/critic/peer-review alongside the five LLM specialists. No specialist subagent is dispatched for it.
62
+ When a prior run exists for the same PR (active or archived under `~/.magpie/`), setup writes `$RUN_DIR/incremental.json` with `{previousRunId, previousSha, currentSha, sameSha}`. The post stage surfaces this as a "Incremental review since `<sha>`" trailer on the summary comment.
48
63
 
49
- The pipeline has no separate context-indexing stage, but `magpie status` and the progress page track one. After setup succeeds, append `{stage: context, status: skipped}` to `$RUN_DIR/log.jsonl`; both treat a skipped stage as behind them, so the pipeline advances to `specialists`.
64
+ Setup also runs a deterministic test-coverage check: when the diff contains zero test or spec files anywhere, each non-test source file with `>= 10` added code lines gets a `domain: "tests"` finding in `$RUN_DIR/findings/tests.json`. That sixth domain flows through dedupe/critic/peer-review alongside the five LLM specialists; no subagent is dispatched for it.
50
65
 
51
66
  ### 2. Serve
52
67
 
@@ -56,9 +71,9 @@ Start the HTML server in the background using the Bash tool with `run_in_backgro
56
71
  magpie serve "$RUN_DIR"
57
72
  ```
58
73
 
59
- Read `$RUN_DIR/state/server-info` for the URL; the server writes it asynchronously at startup, so if the file doesn't exist yet, wait a moment and re-read (it appears within ~1s). Print to the user: "Open <url> in your browser to follow along."
74
+ Read `$RUN_DIR/state/server-info` for the URL; the server writes it asynchronously, so if it is missing, wait a moment and re-read (it appears within ~1s). Print to the user: "Open <url> in your browser to follow along."
60
75
 
61
- The server shuts down after 30 minutes with no requests (an open report tab heartbeats every 30s, so it stays up while the user is looking at it) and deletes `state/server-info` on the way out. Nothing in the pipeline depends on it staying alive: re-run `magpie serve "$RUN_DIR"` to bring the report back.
76
+ The server shuts down after 30 idle minutes (an open report tab heartbeats every 30s, so it stays up while the user is looking) and deletes `state/server-info` on the way out. Nothing in the pipeline depends on it: re-run `magpie serve "$RUN_DIR"` to bring the report back.
62
77
 
63
78
  Render the first progress paint:
64
79
 
@@ -66,53 +81,125 @@ Render the first progress paint:
66
81
  magpie render "$RUN_DIR" progress
67
82
  ```
68
83
 
69
- ### 3. Specialists
84
+ ### 3. Context
85
+
86
+ Append `{stage: context, status: running}` to `$RUN_DIR/log.jsonl` and re-render progress. This stage has two steps and never aborts the run.
87
+
88
+ **Bind probe.** If the `mcp__code-intelligence__*` tools are not in your tool list, skip straight to the scout with `CODE_INTELLIGENCE=unavailable`. Otherwise call `bind_workspace` with `$RUN_DIR/worktree`: it is a linked git worktree, so an already-indexed base repo seeds its index instead of re-indexing.
89
+
90
+ - `consent_required` means the base repo has never completed an index. **Never call `approve_indexing`**: that is a full GPU pass the user did not ask for. Set `CODE_INTELLIGENCE=unavailable`, and print one line: "Code intelligence is unavailable (the base repo has no index); specialists will review from the diff alone."
91
+ - `indexing_started` or `indexing_in_progress` means the seed took. Poll `get_index_stats` every 5s for at most 60s, then set `CODE_INTELLIGENCE=available` either way. Do not block the pipeline on completion; the specialist contract handles a still-indexing tool.
92
+ - A ready result sets `CODE_INTELLIGENCE=available`.
93
+ - Any other error sets `CODE_INTELLIGENCE=unavailable`. Do not retry.
94
+
95
+ **Scout.** Read `references/scout.md` and dispatch one subagent (Agent tool, `general-purpose`) carrying the `magpie-scout` block with `<<RUN_DIR>>`, `<<PR_NUMBER>>`, and `<<CODE_INTELLIGENCE>>` substituted. It writes `$RUN_DIR/brief.json`.
96
+
97
+ Append `{stage: context, status: done, codeIntelligence: true|false}` and re-render progress. If the scout returned without writing `brief.json`, append `{stage: context, status: skipped, codeIntelligence: true|false}` instead and continue: the brief is optional everywhere it is read. Both entries carry the probe's result, which is known whatever the scout did.
98
+
99
+ ### 4. Specialists
70
100
 
71
- Read `references/specialists.md` now, before dispatching anything. It holds the five focus blocks and the output contract that every specialist prompt is built from. Assemble the prompts from that file verbatim: prompts written from memory drift off the JSON contract, and `magpie dedupe` drops findings it cannot parse.
101
+ Read `references/specialists.md` now, before dispatching anything: it holds the five focus blocks and the output contract every specialist prompt is built from. Assemble prompts from that file verbatim; written from memory they drift off the JSON contract, and `magpie dedupe` drops findings it cannot parse.
72
102
 
73
- Append `{stage: specialists, status: running}` to `$RUN_DIR/log.jsonl` and re-render progress, so the served page shows the stage as active rather than "Paused". Then dispatch the five specialist subagents in a single message using five Agent tool calls in parallel, one per focus in (security, bugs, performance, code-smells, architecture), each carrying the prompt that `references/specialists.md` describes.
103
+ Append `{stage: specialists, status: running}` to `$RUN_DIR/log.jsonl` and re-render
104
+ progress, so the served page shows the stage as active rather than "Paused". Then read
105
+ `$RUN_DIR/shards/manifest.json`.
74
106
 
75
- After each subagent returns, append `{stage: specialist, focus: <focus>, status: done, findings: <count>}` to `$RUN_DIR/log.jsonl` and re-render progress. (Per-focus `specialist` entries are diagnostic; only the aggregate `specialists` entry advances `magpie status`.)
107
+ **One shard, zero shards, or no manifest** (a diff filtered down to nothing, e.g. a
108
+ lockfile-only PR, yields `shards: []` in an otherwise normal manifest; a run predating
109
+ this feature has no manifest at all): dispatch the five specialists in a single message,
110
+ five parallel Agent calls, one per focus in (security, bugs, performance, code-smells,
111
+ architecture), each carrying the prompt `references/specialists.md` describes with the
112
+ unsharded run header. The shard gate and the wave dispatch below do not apply; the
113
+ logging and the file check at the end of this stage still do.
76
114
 
77
- If all five specialists fail (no findings files written), log `{stage: specialists, status: error}` and stop. Otherwise mark `{stage: specialists, status: done}`.
115
+ **More than one shard:** each focus reviews every shard, so the run dispatches
116
+ `5 × <shard count>` subagents in total.
78
117
 
79
- ### 4. Dedupe
118
+ **More than four shards: stop and ask the user once, before dispatching anything.**
119
+ State the shard count, the resulting agent count, and the three options: proceed as
120
+ sharded; re-shard for fewer, larger chunks with
121
+ `magpie shard "$RUN_DIR" --budget <lines> --max-files <n>`, raising both flags (a PR of
122
+ many small files is split by the 80-file cap, so a larger `--budget` alone changes
123
+ nothing); or review only the highest-risk shards, which means appending
124
+ `{stage: shard-coverage, status: partial, reviewed: [<ids>], skipped: [<ids>]}` to
125
+ `$RUN_DIR/log.jsonl` and telling the user in the terminal which shards go unreviewed.
126
+ That log entry is the only record of the gap: the report has no unreviewed marker.
127
+ Wait for the answer. This is the only interactive gate in the pipeline before the
128
+ report, and it exists so that neither the cost nor a coverage gap is ever silent.
129
+
130
+ Dispatch by wave, one shard per wave, the five focuses in parallel within a wave,
131
+ re-rendering progress between waves. That holds in-flight agents at five and makes a
132
+ crash cheap to resume: only the `(focus, shard)` pairs whose findings file is missing
133
+ need re-dispatching.
134
+
135
+ After each subagent returns, append
136
+ `{stage: specialist, focus: <focus>, shard: <n>, status: done, findings: <count>}` to
137
+ `$RUN_DIR/log.jsonl` and re-render progress. Omit `shard` on the unsharded path (one
138
+ shard, zero shards, or no manifest).
139
+ (Per-focus `specialist` entries are diagnostic; only the aggregate `specialists` entry
140
+ advances `magpie status`.)
141
+
142
+ Before leaving this stage, list `$RUN_DIR/findings` and confirm one file per expected
143
+ `(focus, shard)` pair: `5 × <shard count>` named `findings/<focus>.shard-<n>.json` when
144
+ sharded, five `findings/<focus>.json` otherwise, plus `findings/tests.json` from setup.
145
+ Re-dispatch any pair missing from a shard you meant to review; a shard skipped at the
146
+ gate is expected to have none. `magpie dedupe` re-checks this against the manifest and
147
+ names every missing pair on stdout, as a backstop rather than a substitute.
148
+
149
+ If every specialist fails (no findings files written), log
150
+ `{stage: specialists, status: error}`, rebind code intelligence to `$REPO` if bound
151
+ (stage 10), and stop. Otherwise mark `{stage: specialists, status: done}`.
152
+
153
+ ### 5. Dedupe
80
154
 
81
155
  ```
82
156
  magpie dedupe "$RUN_DIR" [--threshold <0-10>]
83
157
  ```
84
158
 
85
- `magpie dedupe` also runs a deterministic evidence check against the worktree: findings whose `file` is missing or whose `line` is out of range are dropped. Drops are logged and recorded to `$RUN_DIR/evidence-dropped.json`. The check is skipped if the worktree is no longer present (archived run replay).
159
+ `magpie dedupe` also runs a deterministic evidence check against the worktree: findings whose `file` is missing or whose `line` is out of range are dropped, logged, and recorded to `$RUN_DIR/evidence-dropped.json`. The check is skipped when the worktree is gone (archived run replay).
86
160
 
87
- Each finding receives a derived 0-10 `score` from its risk fields. Findings below `--threshold` (default 3) are dropped before the critic LLM runs and recorded to `$RUN_DIR/threshold-dropped.json`. Pass `--threshold 0` to keep everything.
161
+ Each finding gets a derived 0-10 `score` from its risk fields; those below `--threshold` (default 3) are dropped before the critic LLM runs and recorded to `$RUN_DIR/threshold-dropped.json`. Pass `--threshold 0` to keep everything.
88
162
 
89
163
  Re-render progress.
90
164
 
91
- ### 5. Critic
165
+ ### 6. Critic
92
166
 
93
167
  Read `references/critic.md` and `$RUN_DIR/findings.deduped.json`. Substitute both placeholders in the critic rubric (the compact candidate list including each finding's `onChangedLine`, and the `<<DIFF_EXCERPT>>` hunks for the referenced files), then apply the rubric verbatim (one verdict per finding). Write the kept subset to `$RUN_DIR/findings.kept.json`. Append `{stage: critic, status: done}` and re-render progress.
94
168
 
95
- ### 6. Peer review
169
+ When `findings.deduped.json` holds more than 40 findings, run the rubric in batches of
170
+ 30 rather than one prompt: a sharded run can produce more candidates than fit alongside
171
+ their diff excerpts. Apply the same rubric verbatim per batch and concatenate the kept
172
+ subsets into `findings.kept.json`.
173
+
174
+ ### 7. Peer review
96
175
 
97
- Append `{stage: peer-review, status: running}` to `$RUN_DIR/log.jsonl` and re-render progress. This stage always runs. `codex` is the preferred reviewer because it is a different model from the Claude agents that produced the findings; when `codex` is unavailable, a Claude second-opinion subagent stands in.
176
+ Append `{stage: peer-review, status: running}` to `$RUN_DIR/log.jsonl` and re-render progress. This stage always runs. `codex` is preferred because it is a different model from the Claude agents that produced the findings; without it, a Claude second-opinion subagent stands in.
98
177
 
99
- Build the peer-review prompt first: read `references/peer-review.md`, take the `magpie-peer-review` block from it, and substitute the placeholders listed in that file's `## Substitute before use` preamble. Write the substituted prompt to `$RUN_DIR/peer-prompt.md`.
178
+ Build the peer-review prompt first: read `references/peer-review.md`, take the `magpie-peer-review` block from it, and substitute the placeholders listed in that file's `## Substitute before use` preamble.
100
179
 
101
- **Codex path (preferred).** If `codex` is available (setup did not log `missingOptional: ["codex"]` and `command -v codex` succeeds), set `<<PEER_PROVIDER>>` to `codex` and run codex with the prompt piped on stdin:
180
+ One batch carries up to 40 findings; above that, split them 30 at a time, as in stage 6.
181
+ Write each batch's prompt, its `<<KEPT_FINDINGS_COMPACT>>` narrowed to that batch, to
182
+ `$RUN_DIR/peer-prompt-<k>.md`, `<k>` counting from 1. **When there is a single batch, drop `-<k>` throughout** (`peer-prompt.md`,
183
+ `peer.out`), which is the common case. Keep the `add` id counter running across batches
184
+ (`peer-1`, `peer-2`, ...): restarting it per batch produces colliding ids, and every
185
+ finding in `findings.final.json` must have a unique one or the report and post stages
186
+ crash.
187
+
188
+ **Codex path (preferred).** If `codex` is available (setup did not log `missingOptional: ["codex"]` and `command -v codex` succeeds), set `<<PEER_PROVIDER>>` to `codex` and run codex once per batch, that batch's prompt piped on stdin:
102
189
 
103
190
  ```
104
- codex exec < "$RUN_DIR/peer-prompt.md" > "$RUN_DIR/peer.out"
191
+ codex exec < "$RUN_DIR/peer-prompt-<k>.md" > "$RUN_DIR/peer-<k>.out"
105
192
  ```
106
193
 
107
- `peer.out` is codex's full transcript; extract the fenced JSON block tagged `review-peer-review` from it to get the verdicts array. Write that verdicts array to `$RUN_DIR/peer.json`, append `{stage: peer-review, status: done, provider: codex}`, then apply the verdicts as described below.
194
+ Each `peer-<k>.out` is codex's full transcript; extract the fenced JSON block tagged `review-peer-review` from each. Write the concatenated verdict arrays to `$RUN_DIR/peer.json` once, after the last batch: writing `peer.json` per batch keeps only the last batch's verdicts and silently discards the rest. Then append `{stage: peer-review, status: done, provider: codex}` and apply the verdicts as described below.
108
195
 
109
- If codex returns non-zero, do not abort: record `{stage: peer-review, provider: codex, status: fallback, error: "<first line of stderr>"}` and fall through to the Claude path. (Never log `status: error` for a recoverable codex failure: `magpie status` stops at the first `error` entry and would report the run as poisoned even after the Claude fallback succeeds.)
196
+ If codex returns non-zero on a batch, do not abort: record `{stage: peer-review, provider: codex, status: fallback, batch: <k>, error: "<first line of stderr>"}` and take the Claude path for that batch. (Never log `status: error` for a recoverable codex failure: `magpie status` stops at the first `error` entry and would report the run as poisoned even after the Claude fallback succeeds.)
110
197
 
111
- **Claude path (fallback).** When `codex` is unavailable or failed, get the second opinion from a Claude subagent instead. Set `<<PEER_PROVIDER>>` to `claude`, then prepend the `magpie-peer-review-claude-preamble` block from `references/peer-review.md` to the substituted peer-review prompt (the preamble forces genuine independence, since the reviewer shares a model family with the primary reviewers). Dispatch one subagent (Agent tool, `general-purpose`) whose entire task is that combined prompt, and instruct it to return only the fenced `review-peer-review` JSON block. Write its output to `$RUN_DIR/peer.out`, extract the `review-peer-review` block to `$RUN_DIR/peer.json`, and append `{stage: peer-review, status: done, provider: claude}`.
198
+ **Claude path (fallback).** When `codex` is unavailable or failed, get the second opinion from a Claude subagent instead, one per batch. Set `<<PEER_PROVIDER>>` to `claude`, then prepend the `magpie-peer-review-claude-preamble` block from `references/peer-review.md` to each batch's substituted prompt (the preamble forces genuine independence, since the reviewer shares a model family with the primary reviewers). Dispatch one subagent (Agent tool, `general-purpose`) per batch whose entire task is that combined prompt, and instruct it to return only the fenced `review-peer-review` JSON block. Write each output to `$RUN_DIR/peer-<k>.out`, extract each `review-peer-review` block, merge into `$RUN_DIR/peer.json` after the last batch as above, and append `{stage: peer-review, status: done, provider: claude}` (`provider: mixed` if codex handled some batches).
112
199
 
113
- **Apply the verdicts (both paths).** Parse the verdicts JSON and apply the `update` / `add` entries (an empty array means no change). For each `add`, mint a unique `id` on the new finding before merging (`peer-1`, `peer-2`, ...): the peer contract does not include ids, but every finding in `findings.final.json` must carry one or the report render and post stages will crash. Then write `findings.final.json`. Re-render progress.
200
+ **Apply the verdicts (both paths).** Parse the merged verdicts and apply the `update` / `add` entries (an empty array means no change). Mint each `add`'s `id` as above before merging, since the peer contract does not carry ids. Then write `findings.final.json`. Re-render progress.
114
201
 
115
- ### 7. Report
202
+ ### 8. Report
116
203
 
117
204
  ```
118
205
  magpie render "$RUN_DIR" findings
@@ -124,11 +211,11 @@ Print to the terminal: "Findings ready at <url>. Tick the ones you want and clic
124
211
 
125
212
  End the turn.
126
213
 
127
- ### 8. Post
214
+ ### 9. Post
128
215
 
129
- Most users will tick the checkboxes in the served report and click **Post Selected** (or **Post Recommended**, which takes every finding whose `risk.action` is `must-fix` or `should-fix`, skipping the `consider`/`optional` ones); the report server handles the rest and posts the batch as one GitHub review with inline threads. The agent only handles posts when the user explicitly types `post` (optionally `post 1,3,7` for indices) in the conversation, which takes the CLI path below: separate inline comments plus a top-level summary comment. Either path records posted ids in `post-status.json`, so the two cannot double-post the same finding.
216
+ Most users tick the checkboxes in the served report and click **Post Selected** (or **Post Recommended**, which takes every `must-fix`/`should-fix` finding and skips the `consider`/`optional` ones); the server posts that batch as one GitHub review with inline threads. The agent posts only when the user types `post` (optionally `post 1,3,7` for indices), which takes the CLI path below: separate inline comments plus a top-level summary comment. Either path records posted ids in `post-status.json`, so the two cannot double-post the same finding.
130
217
 
131
- When the user types `post`, read `$RUN_DIR/state/events`. Fold the events in order, keeping the LAST event per finding id; ids whose last event is `select` are selected. (Not union-minus: the UI emits one event per toggle, so select then deselect then select again must resolve to selected.) Merge with any explicit indices the user named (1-based, against `findings.final.json` in file order). If that leaves nothing selected, say so and ask rather than posting an empty batch. Then post via the CLI:
218
+ When the user types `post`, read `$RUN_DIR/state/events` and fold them in order, keeping the LAST event per finding id; ids whose last event is `select` are selected. (Not union-minus: the UI emits one event per toggle, so select, deselect, select again resolves to selected.) Merge any explicit indices the user named (1-based, against `findings.final.json` in file order). If nothing is selected, say so and ask rather than posting an empty batch. Then post via the CLI:
132
219
 
133
220
  ```
134
221
  magpie post "$RUN_DIR" --ids id1,id2,id3
@@ -136,30 +223,32 @@ magpie post "$RUN_DIR" --ids id1,id2,id3
136
223
 
137
224
  That delegates to `runPost`, which:
138
225
 
139
- - Picks `formatInlineBody` (severity heading, `<sub>` risk metaline, parsed `Observation`/`Why it matters`/`Suggested direction`/`Needs verification` sections, optional `` ```suggestion `` block, hidden `magpie:finding` marker) when the finding has a `line`, and uses `gh api repos/<owner>/<repo>/pulls/<n>/comments` to open an inline review thread.
140
- - Falls back to `formatConversationBody` (same shape plus a `Location · <file>:<line>` metaline) posted via `gh pr comment <n>` when there is no anchor, or when GitHub rejects the inline anchor with 422.
141
- - When at least one new finding is being posted in this batch (default `auto` mode), prepends one top-level summary comment (verdict line, "Needs Attention" top three, `<details>` risk breakdown) and persists the sentinel `__summary__` in `post-status.json` so re-runs don't duplicate it. Override with `--include-summary always|never` if you need to force or suppress it.
226
+ - For a finding with a `line`, picks `formatInlineBody` (severity heading, `<sub>` risk metaline, parsed `Observation`/`Why it matters`/`Suggested direction`/`Needs verification` sections, optional `` ```suggestion `` block, hidden `magpie:finding` marker) and opens an inline review thread via `gh api repos/<owner>/<repo>/pulls/<n>/comments`.
227
+ - Falls back to `formatConversationBody` (same shape plus a `Location · <file>:<line>` metaline) via `gh pr comment <n>` when there is no anchor, or GitHub rejects the inline anchor with 422.
228
+ - When at least one new finding is in the batch (default `auto` mode), prepends one top-level summary comment (verdict line, "Needs Attention" top three, `<details>` risk breakdown) and persists the `__summary__` sentinel in `post-status.json` so re-runs don't duplicate it. Override with `--include-summary always|never`.
142
229
  - Appends `{stage: post, ...}` events to `log.jsonl` and updates `$RUN_DIR/post-status.json` per finding id.
143
230
 
144
- Pass `--dry-run` to record the would-be gh commands without invoking gh. After posting, append `{stage: post, status: done}` to `$RUN_DIR/log.jsonl` (`runPost` logs per-finding `ok`/`failed` events but not the stage-complete marker, and `magpie status` counts only `done`), then re-render the report so the badges update:
231
+ Pass `--dry-run` to record the would-be gh commands without invoking gh. After posting, append `{stage: post, status: done}` to `$RUN_DIR/log.jsonl` (`runPost` logs per-finding `ok`/`failed` events but not the stage marker, and `magpie status` counts only `done`), then re-render the report so the badges update:
145
232
 
146
233
  ```
147
234
  magpie render "$RUN_DIR" findings
148
235
  ```
149
236
 
150
- ### 9. Cleanup
237
+ ### 10. Cleanup
151
238
 
152
239
  ```
153
240
  magpie cleanup "$RUN_DIR" --repo "$REPO"
154
241
  ```
155
242
 
156
- The run directory is renamed to `<run-dir>.archived-<timestamp>` and the worktree is removed. The CLI prints two lines on success: `archived to <path>` and `view later: magpie open <archived-id>`. Surface that second line to the user verbatim so they have a one-command path back to the report.
243
+ If the context stage bound code intelligence, rebind the session now: call `bind_workspace` with `$REPO`. Binding is per session with no per-call override, so ending a run without this leaves the session pointed at a worktree `cleanup` just deleted. The daemon prunes the seeded index once the worktree is gone.
244
+
245
+ The run directory is renamed to `<run-dir>.archived-<timestamp>` and the worktree is removed. The CLI prints two lines on success: `archived to <path>` and `view later: magpie open <archived-id>`. Surface that second line verbatim so the user has a one-command path back to the report.
157
246
 
158
247
  The archived `findings.html` is self-contained and auto-switches to read-only "archived" mode when opened, so:
159
248
 
160
- - `magpie open` (no args) opens the latest run in the user's default browser via `open`/`xdg-open`. Add `--dry-run` to see the command without spawning.
249
+ - `magpie open` (no args) opens the latest run in the default browser via `open`/`xdg-open`; `--dry-run` prints the command instead of spawning it.
161
250
  - `magpie open <id>` opens a specific archived run.
162
- - `magpie serve <id>` re-spins the Bun server against an archived run if the user wants the live interactive surface back (posts still work because `pr.json` retains the head SHA).
251
+ - `magpie serve <id>` re-spins the Bun server against an archived run for the live interactive surface (posts still work: `pr.json` retains the head SHA).
163
252
  - `magpie --list-runs` enumerates all runs in `~/.magpie/`.
164
253
 
165
254
  ## Resuming a crashed run
@@ -172,13 +261,17 @@ magpie status "$RUN_DIR"
172
261
 
173
262
  The JSON output tells you `lastCompleted` and `next`. Resume from `next`:
174
263
 
175
- - `context` is a no-op. Append `{stage: context, status: skipped}` and continue at `specialists`.
264
+ - `context` re-runs by redoing the bind probe, then dispatching the scout only if `$RUN_DIR/brief.json` is missing. The seeded index survives a crash, so the rebind is near-instant.
176
265
  - Any other stage: run it as written in the walkthrough.
177
- - If a specialist focus has no findings file but its sibling stages are done, re-dispatch only that focus.
266
+ - If a specialist focus has no findings file but its sibling stages are done,
267
+ re-dispatch only that focus. On a sharded run the unit is the `(focus, shard)` pair:
268
+ read `shards/manifest.json`, and re-dispatch only the pairs with no
269
+ `findings/<focus>.shard-<n>.json`. Never re-shard mid-run without deleting those
270
+ files first (stage 1): the check would otherwise trust ids that moved.
178
271
  - Non-null `error` means the run stopped on a failed stage. Report which stage to the user and confirm before re-running it.
179
272
 
180
- The server from the original run is gone. Restart it with `magpie serve "$RUN_DIR"` (step 2) before re-rendering, so the user gets a live URL again.
273
+ The original server is gone. Restart it with `magpie serve "$RUN_DIR"` (step 2) before re-rendering, so the user gets a live URL again.
181
274
 
182
275
  ## Aborting
183
276
 
184
- If the user types `abort` mid-run, run `magpie cleanup` immediately and exit.
277
+ If the user types `abort` mid-run, rebind code intelligence to `$REPO` if bound (stage 10), then run `magpie cleanup` and exit.
@@ -10,6 +10,8 @@ Subcommands:
10
10
  setup <run-dir> --pr <n> Pre-flight, fetch PR, create worktree
11
11
  serve <run-dir-or-id> Start the HTML server (accepts active or archived run id)
12
12
  dedupe <run-dir> Merge specialist findings into deduped set
13
+ shard <run-dir> [--budget N] [--max-files N]
14
+ Re-split diff.patch into budgeted shards
13
15
  render <run-dir> <page> Render progress.html or findings.html
14
16
  cleanup <run-dir> Remove worktree, stop server, archive run
15
17
  status <run-dir> Print highest completed stage
@@ -116,6 +118,47 @@ const HANDLERS: Record<string, Handler> = {
116
118
  const { runDedupe } = await import('../scripts/dedupe-cmd.ts')
117
119
  return runDedupe(runDir, threshold !== undefined ? { threshold } : {})
118
120
  },
121
+ shard: async (args) => {
122
+ const runDir = args[0]
123
+ if (!runDir) {
124
+ process.stderr.write('shard: missing <run-dir> [--budget <n>] [--max-files <n>]\n')
125
+ return 2
126
+ }
127
+ const numFlag = (name: string): number | undefined | null => {
128
+ const idx = args.indexOf(name)
129
+ if (idx === -1) return undefined
130
+ const n = Number(args[idx + 1])
131
+ if (!Number.isFinite(n) || n <= 0) {
132
+ process.stderr.write(`shard: invalid ${name} ${args[idx + 1]} (want a positive number)\n`)
133
+ return null
134
+ }
135
+ return n
136
+ }
137
+ const budget = numFlag('--budget')
138
+ if (budget === null) return 2
139
+ const maxFiles = numFlag('--max-files')
140
+ if (maxFiles === null) return 2
141
+ // `shardDiff` tolerates a missing diff.patch (setup calls it before the diff
142
+ // can exist) and mkdirs `shards/` on the way, so a typo'd run directory would
143
+ // otherwise be invented on disk and reported as a successful zero-shard split.
144
+ const { stat } = await import('node:fs/promises')
145
+ const isRunDir = await stat(runDir)
146
+ .then((s) => s.isDirectory())
147
+ .catch(() => false)
148
+ if (!isRunDir) {
149
+ process.stderr.write(`shard: no such run directory: ${runDir}\n`)
150
+ return 2
151
+ }
152
+ const { shardDiff } = await import('../scripts/shard.ts')
153
+ const manifest = await shardDiff(runDir, {
154
+ ...(budget !== undefined ? { budget } : {}),
155
+ ...(maxFiles !== undefined ? { maxFiles } : {}),
156
+ })
157
+ process.stdout.write(
158
+ `${manifest.shards.length} shard(s), ${manifest.totalFiles} files, ${manifest.totalLines} patch lines\n`,
159
+ )
160
+ return 0
161
+ },
119
162
  render: async (args) => {
120
163
  const runDir = args[0]
121
164
  const page = args[1]
@@ -0,0 +1,18 @@
1
+ {
2
+ "purpose": "Adds bounded retries to the upload path so transient S3 failures stop surfacing to end users as hard errors.",
3
+ "changes": [
4
+ "Wraps the S3 put call in a bounded retry with jittered backoff",
5
+ "Threads a per-request deadline through the storage client",
6
+ "Adds a metrics counter for exhausted retry budgets"
7
+ ],
8
+ "subsystems": [
9
+ { "name": "upload", "role": "owns the client-facing put path" },
10
+ { "name": "storage-client", "role": "wraps the S3 SDK and owns timeouts" }
11
+ ],
12
+ "watchItems": [
13
+ "The PR body claims the retry is idempotent, but no idempotency key is sent with the put"
14
+ ],
15
+ "unclear": [
16
+ "Whether the new per-request deadline interacts correctly with the outer HTTP timeout"
17
+ ]
18
+ }
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+ # Fake gh whose `pr diff` refuses to serve, the way GitHub does above ~300
3
+ # files. MAGPIE_FAKE_HEAD_OID lets a test point pr.json at a real local commit.
4
+ # MAGPIE_FAKE_DIFF_MODE=fail (default) exits non-zero; =empty exits 0 with no
5
+ # output, which is the truncation case fetchPr also has to catch.
6
+ case "$1 $2" in
7
+ "pr view")
8
+ cat <<JSON
9
+ {
10
+ "number": 7,
11
+ "title": "Fake big PR",
12
+ "headRefName": "feature-x",
13
+ "baseRefName": "main",
14
+ "headRefOid": "${MAGPIE_FAKE_HEAD_OID:-deadbeefdeadbeefdeadbeefdeadbeefdeadbeef}",
15
+ "baseRefOid": "cafebabecafebabecafebabecafebabecafebabe",
16
+ "author": { "login": "octocat" },
17
+ "body": "Fake body",
18
+ "url": "https://github.com/octocat/Hello-World/pull/7",
19
+ "files": [
20
+ { "path": "a.ts", "additions": 1, "deletions": 1, "changeType": "modified" }
21
+ ],
22
+ "commits": [],
23
+ "closingIssuesReferences": []
24
+ }
25
+ JSON
26
+ ;;
27
+ "pr diff")
28
+ if [ "${MAGPIE_FAKE_DIFF_MODE:-fail}" = "empty" ]; then
29
+ exit 0
30
+ fi
31
+ echo "the diff exceeded the maximum number of files (300) (HTTP 406)" >&2
32
+ exit 1
33
+ ;;
34
+ *)
35
+ echo "fake-gh-nodiff: unsupported args: $*" >&2
36
+ exit 1
37
+ ;;
38
+ esac
@@ -19,6 +19,20 @@ case "$1 $2" in
19
19
  "deletions": 0,
20
20
  "changeType": "modified"
21
21
  }
22
+ ],
23
+ "commits": [
24
+ {
25
+ "oid": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
26
+ "messageHeadline": "Add retry handling to the upload path",
27
+ "messageBody": ""
28
+ }
29
+ ],
30
+ "closingIssuesReferences": [
31
+ {
32
+ "number": 42,
33
+ "title": "Uploads fail intermittently under load",
34
+ "url": "https://github.com/octocat/Hello-World/issues/42"
35
+ }
22
36
  ]
23
37
  }
24
38
  JSON
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magpie",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,9 +1,14 @@
1
1
  # Peer-review prompt
2
2
 
3
- The agent substitutes the placeholders below and writes the result to `<run-dir>/peer-prompt.md`. Step 6 then feeds that prompt to the peer reviewer: `codex exec < <run-dir>/peer-prompt.md > <run-dir>/peer.out` when codex is available, or a Claude `general-purpose` subagent (with the `magpie-peer-review-claude-preamble` prepended) writing to `<run-dir>/peer.out` when it is not.
3
+ The agent substitutes the placeholders below and writes the result to `<run-dir>/peer-prompt.md`. Stage 7 then feeds that prompt to the peer reviewer: `codex exec < <run-dir>/peer-prompt.md > <run-dir>/peer.out` when codex is available, or a Claude `general-purpose` subagent (with the `magpie-peer-review-claude-preamble` prepended) writing to `<run-dir>/peer.out` when it is not.
4
4
 
5
5
  Either way, extract the fenced `review-peer-review` block from `peer.out` and save it to `<run-dir>/peer.json`.
6
6
 
7
+ The filenames above are the single-batch case. Stage 7 batches above 40 findings, and
8
+ those runs use `peer-prompt-<k>.md` and `peer-<k>.out` per batch, one reviewer
9
+ invocation each, with every batch's verdicts merged into one `peer.json` after the
10
+ last batch. Everything below is per batch and otherwise unchanged.
11
+
7
12
  ## Substitute before use
8
13
 
9
14
  Replace each `<<NAME>>` placeholder in the block below:
@@ -72,7 +77,7 @@ Rules:
72
77
 
73
78
  ## Claude peer-review preamble
74
79
 
75
- Used only by the Claude fallback path in step 6. Prepend this block verbatim (no substitutions) to the substituted `magpie-peer-review` prompt before dispatching the subagent. Its job is to buy back the independence you lose by using the same model family that produced the findings: the reviewer must re-derive each verdict from the diff rather than trusting the finding text, and must actively resist rubber-stamping.
80
+ Used only by the Claude fallback path in stage 7. Prepend this block verbatim (no substitutions) to the substituted `magpie-peer-review` prompt before dispatching the subagent. Its job is to buy back the independence you lose by using the same model family that produced the findings: the reviewer must re-derive each verdict from the diff rather than trusting the finding text, and must actively resist rubber-stamping.
76
81
 
77
82
  ````magpie-peer-review-claude-preamble
78
83
  You are a fresh, independent second-opinion reviewer. You have no memory of, and no stake in, how the findings below were produced. They were generated by other agents that share your model family, so they may carry the same blind spots you would: do not defer to them, and do not assume they are correct because they sound confident.
@@ -0,0 +1,90 @@
1
+ # Scout prompt
2
+
3
+ Stage 3 of the walkthrough dispatches one subagent from this file, before the five
4
+ specialists. Send the fenced `magpie-scout` block below as the agent's entire task,
5
+ with `<<RUN_DIR>>` and `<<PR_NUMBER>>` replaced by the real values first. The
6
+ subagent has no shell variables from your session, so an unexpanded path means it
7
+ writes the brief where nothing will read it.
8
+
9
+ Substitute `<<CODE_INTELLIGENCE>>` with `available` when the bind probe succeeded and
10
+ `unavailable` otherwise. The scout still runs when code-intelligence is unavailable;
11
+ only the subsystem map degrades.
12
+
13
+ ```magpie-scout
14
+ You are a senior engineer building the orienting brief that five specialist
15
+ reviewers will read before they review PR #<<PR_NUMBER>>. You are not reviewing the
16
+ code. You are answering "what is this PR for, and what does it actually do".
17
+
18
+ Working directory: <<RUN_DIR>>/worktree
19
+ PR metadata: <<RUN_DIR>>/pr.json
20
+ Diff: <<RUN_DIR>>/diff.patch
21
+ Code intelligence: <<CODE_INTELLIGENCE>>
22
+
23
+ ## What to read
24
+
25
+ 1. `pr.json` for the author's stated intent: `title`, `body`, `commits[].messageHeadline`,
26
+ and `closingIssuesReferences[].title`. This is the claim.
27
+ 2. `diff.patch` for what the change actually does. This is the evidence.
28
+ 3. The worktree for surrounding context on any file the diff changes but does not
29
+ explain.
30
+
31
+ ## Code intelligence
32
+
33
+ When the line above says `available`, the code-intelligence MCP tools are indexed
34
+ against `<<RUN_DIR>>/worktree`, seeded from the base repository. Call `bind_workspace`
35
+ with `<<RUN_DIR>>/worktree` before your first query. Use them to map where the change
36
+ lands:
37
+
38
+ - `get_module_summary` on each directory the diff touches, to name the subsystem and
39
+ say what it is responsible for.
40
+ - `explore_dependency_graph` on the touched modules, to say what depends on them.
41
+
42
+ If a tool returns `indexing_in_progress`, finish reading the diff and retry once. If
43
+ it still is not ready, or the line above says `unavailable`, write `"subsystems": []`
44
+ and carry on. Never call `approve_indexing`. Never call `refresh_index`. Triggering a
45
+ full index is a consent-gated operation that is not yours to start.
46
+
47
+ ## How to reason
48
+
49
+ 1. State the purpose in your own words, not the author's. If you cannot restate it
50
+ without quoting the PR body, you have not understood it yet.
51
+ 2. Group the diff into 3-7 concerns. A concern is a thing a reviewer would evaluate
52
+ as a unit, not a file.
53
+ 3. Compare the claim against the evidence. Where the diff does something the stated
54
+ intent does not cover, or omits something the stated intent implies, that is a
55
+ watch item.
56
+ 4. Be honest about what you could not determine. An empty `unclear` on a large PR is
57
+ not credible.
58
+
59
+ ## Output contract
60
+
61
+ Write `<<RUN_DIR>>/brief.json` before returning. The file MUST be a JSON object with
62
+ exactly these five keys:
63
+
64
+ {
65
+ "purpose": string, // 1-3 sentences: what this PR is for, in your words. Required
66
+ // and non-empty; a brief with no purpose is discarded whole.
67
+ "changes": string[], // 3-7 entries: what it actually does, grouped by concern.
68
+ // One clause each, no trailing period needed.
69
+ "subsystems": [ { "name": string, "role": string } ],
70
+ // Code-intelligence derived. `name` is the subsystem, `role`
71
+ // is one clause on what it is responsible for. [] when
72
+ // code intelligence is unavailable.
73
+ "watchItems": string[], // Where the diff and the stated intent diverge, or where the
74
+ // intent implies a risk the diff does not address. Often
75
+ // empty. See the boundary below.
76
+ "unclear": string[] // What you could not determine from the bundle.
77
+ }
78
+
79
+ A watch item is not a finding. You do not assign severity, you do not assign risk,
80
+ and you do not recommend a fix. A watch item is a pointer a specialist may escalate
81
+ into a finding in its own domain, with its own risk fields, or dismiss. Write it as
82
+ an observation: "the PR body claims X, but the diff does Y".
83
+
84
+ Do not add keys. Do not omit keys; write `[]` for an empty list. Do not transcribe
85
+ commit messages or issue titles into the brief: the report reads those from `pr.json`
86
+ directly, and repeating them wastes the specialists' attention.
87
+
88
+ Return as your final tool result a single line:
89
+ `brief: <N> changes, <M> subsystems, <K> watch items`. Do not include other prose.
90
+ ```