@feigi/fleet-ctl 3.99.3 → 3.100.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.
@@ -32,6 +32,16 @@ Check each added or modified comment for:
32
32
  - **Comments that add no value** — pure restatement of the code, flaggable
33
33
  for removal but not a defect.
34
34
 
35
+ Your dispatch may also name a file holding the PR body and the PR's commit
36
+ messages, as they stood when the review began. Check their factual assertions
37
+ about the change the way you check a comment's: a count, a file or function
38
+ named, what the change touches or leaves alone, a test said to cover it. A
39
+ claim about code the change does not touch is not a finding — ask whether
40
+ reading the change itself would falsify the claim, and file only if it would.
41
+ A finding on a body names its surface at the start of its `claim` ("PR body:"
42
+ or "commit <sha>:") and carries no `file` and no `line`, because there is none
43
+ to give.
44
+
35
45
  Verify by reading the actual referenced code, not by assuming the comment is
36
46
  accurate because it reads plausibly. A claim you did not check against the
37
47
  tree belongs at severity `suggestion`, never `critical` or `important`.
@@ -103,3 +103,4 @@ Tell readers **`git show <sha>:<path>` is the source of truth** — the object s
103
103
  - **Distrust negative claims hardest.** "Nothing else references this", "the sweep is clean" — most likely false, least likely checked: a grep that found nothing looks like a grep never run. Make the specialist state its search scope. Negative claim vs specific finding with paths → paths win.
104
104
  - **Mutation-verify any "this test pins X".** Apply the mutation, confirm *that* test fails — a valid red, and a kill only against the green run on the unmutated tree, as step 2's refuter rules define them — revert. Then one it should **not** catch, staying green — else you proved it fails, not that it discriminates. **One syntactic form is not the class:** a stripper killing `// whole-line` passed a guard that `code; // trailing` walked through.
105
105
  - **Comments are findings**, and the most common one. A comment asserting what the code does not do is a defect. One run shipped five: an invented citation, a mis-stated failure mode, a test file named as covering what it had zero coverage of, an overstated cast fix, a claim its own dependency's source contradicts. Check every added assertion against the tree — **including comments in files this diff does not touch** but whose claims it falsifies (test-name references, "N of 3" counts, tracking-issue pointers).
106
+ - **A finding on a PR body or a commit body** names that surface in its `claim` ("PR body:", or "commit <sha>:") and carries no `file` and no `line`, so `dispositions-check.mjs` presumes it in scope. A false claim in the PR body is corrected there, by editing it with `gh pr edit`. **A commit body cannot be rewritten** — never rewrite history to correct one: correct a false commit-body claim in the PR body instead, say there what the change really does, and that counts as applying the finding. The finding still stands as the review's record: correcting the claim does not withdraw the finding.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feigi/fleet-ctl",
3
- "version": "3.99.3",
3
+ "version": "3.100.0",
4
4
  "description": "Agent fleet: run-team controller, merge bot, PR reviewer, and the ticket pipeline they share",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -1,7 +1,7 @@
1
1
  // review-core.mjs — the review body for PR review.
2
2
  //
3
3
  // This file holds every host-independent declaration PR review needs: the
4
- // JSON schemas, `usableDiff`, `readRules`, `resolveTestCmd`,
4
+ // JSON schemas, `usableDiff`, `usableMessages`, `messagesNote`, `refuterMessagesNote`, `readRules`, `resolveTestCmd`,
5
5
  // `decodeArgs`, `selectDimensions`, `resolveDimensions`, `snapshotMissing`,
6
6
  // `installReason`/`installFinding`, `suiteVerdictOf`/`unrunCrashed`,
7
7
  // `sharedRunNote`, `copyRule`,
@@ -123,6 +123,11 @@ export const SNAPSHOT_SCHEMA = {
123
123
  diffStats: { type: "string" },
124
124
  diffPath: { type: "string" },
125
125
  diffLines: { type: "integer" },
126
+ // Present only when the capture block's `gh pr view … --json body,commits`
127
+ // exited 0, the way `diffPath` stands for `gh pr diff`: it says the file
128
+ // of PR and commit messages was written. `usableMessages` rebuilds the
129
+ // path from the run root, so this field decides only whether it exists.
130
+ messagesPath: { type: "string" },
126
131
  refHead: { type: "string" },
127
132
  prHead: { type: "string" },
128
133
  testCmd: { type: "string" },
@@ -224,7 +229,7 @@ An inconclusive result is no evidence either way: file no finding above \`sugges
224
229
  key: "comments",
225
230
  agentType: "fleet-review-comments",
226
231
  prompt:
227
- "every added factual assertion checked against the tree, INCLUDING comments in files this diff does not touch but whose claims it falsifies (test-name references, 'N of 3' counts, tracking-issue pointers)",
232
+ "every added factual assertion checked against the tree, INCLUDING comments in files this diff does not touch but whose claims it falsifies (test-name references, 'N of 3' counts, tracking-issue pointers), and every factual assertion the PR body and the commit messages make about this change",
228
233
  },
229
234
  {
230
235
  key: "types",
@@ -294,6 +299,63 @@ export function usableDiff(snap) {
294
299
  return `${snap.runRoot}/pr.diff`;
295
300
  }
296
301
 
302
+ // The PR body and the commit messages, saved beside `pr.diff` once when the
303
+ // snapshot is cut, so a body edited later changes nothing this review read.
304
+ // Rebuilt from the checked run root for the reason `usableDiff` is: the path
305
+ // is the caller's to derive, the reported field only says the capture ran.
306
+ export function usableMessages(snap) {
307
+ if (!snap.messagesPath) return null;
308
+ return `${snap.runRoot}/pr-messages.md`;
309
+ }
310
+
311
+ // Where the saved bodies are and how the file reads: the reviewer that files a
312
+ // finding on a body and every refuter that checks one both need it, so it is
313
+ // written once here.
314
+ function messagesFile(messagesPath) {
315
+ return `The PR body and the PR's commit messages are at ${messagesPath}, saved when this
316
+ review began — a later edit changes nothing you read. The file opens with a
317
+ \`=== PR body ===\` line and then one \`=== commit <sha> ===\` line per commit,
318
+ each followed by that commit's headline and body (GitHub splits a headline over
319
+ 72 characters with a \`…\` ending the headline and starting the body). gh lists
320
+ at most a PR's first 100 commits, so a file with 100 \`=== commit\` lines may
321
+ lack the later ones, those nearest the head: a commit it does not hold was not
322
+ read, never checked clean. Read it bounded, like the diff.`;
323
+ }
324
+
325
+ // The paragraph only the comments dimension's dispatch carries: the other
326
+ // dimensions read the diff and the tree, never the words the implementer
327
+ // submitted beside them. With no capture it says so, so the dimension never
328
+ // reads an absent file as a clean body.
329
+ export function messagesNote(messagesPath) {
330
+ if (!messagesPath)
331
+ return `The PR body and commit messages were NOT captured for this review: check none of them, and say so in \`scope_searched\`.`;
332
+ return `${messagesFile(messagesPath)}
333
+
334
+ Check every factual assertion the PR body or a commit message makes about THIS
335
+ change — a count, a file or function named, what the change does or does not
336
+ touch, a test said to cover it — against the snapshot and the diff, exactly as
337
+ you check a comment's. A claim about code the change does not touch is not a
338
+ finding: the test is whether reading the change itself would falsify the
339
+ claim. A finding on a body names its surface at the start of its \`claim\` — "PR
340
+ body:", or "commit <sha>:" with the sha from its \`===\` line — and carries no
341
+ \`file\` and no \`line\`.`;
342
+ }
343
+
344
+ // The same file, named to the refuters of a comments finding. A finding on a
345
+ // body carries no file and no line, and the snapshot holds neither body, so
346
+ // without this a refuter has nothing to check the claim against but a live
347
+ // `gh pr view` — the body as edited since, not the one the review read.
348
+ export function refuterMessagesNote(messagesPath) {
349
+ if (!messagesPath)
350
+ return `The PR body and commit messages were NOT captured for this review, so a claim on "PR body:" or "commit <sha>:" has no saved copy to be checked against.`;
351
+ return `${messagesFile(messagesPath)}
352
+
353
+ A claim that starts "PR body:" or "commit <sha>:" is about that file, which is
354
+ why it names no \`file\` and no \`line\`: check the quoted assertion there, against
355
+ the snapshot and the diff — never against a live \`gh pr view\`, which reads the
356
+ body as it stands now rather than as this review read it.`;
357
+ }
358
+
297
359
  export function readRules(diffPath, stats, snap) {
298
360
  let change;
299
361
  if (diffPath) {
@@ -1091,6 +1153,7 @@ would pick for any command run in that checkout — 'origin', unless an
1091
1153
  REPO=$( { cd ${worktree} && gh repo view --json url -q .url; } 2>"$RUN"/repo.err )
1092
1154
  if [ -n "$REPO" ]; then
1093
1155
  gh pr diff ${pr} -R "$REPO" > "$RUN"/pr.diff
1156
+ gh pr view ${pr} -R "$REPO" --json body,commits -q '"=== PR body ===\\n" + .body + "\\n" + ([.commits[] | "=== commit " + .oid + " ===\\n" + .messageHeadline + "\\n" + .messageBody + "\\n"] | join(""))' > "$RUN"/pr-messages.md || { rm -f "$RUN"/pr-messages.md; echo SNAPSHOT_MESSAGES_FAILED; }
1094
1157
  crossRepo=$(gh pr view ${pr} -R "$REPO" --json isCrossRepository -q .isCrossRepository)
1095
1158
  branch=$(gh pr view ${pr} -R "$REPO" --json headRefName -q .headRefName)
1096
1159
  ref=""
@@ -1109,11 +1172,19 @@ name the worktree's repository, and the controller's own repository is never a
1109
1172
  substitute. Report \`repoUnresolved\` = every line the block printed after
1110
1173
  SNAPSHOT_REPO_UNRESOLVED, verbatim — gh's own reason, such as no remote, or no
1111
1174
  remote on a GitHub host it is logged in to — or 'no error output' if it printed
1112
- none. Omit \`diffPath\`, \`diffLines\`, \`refHead\` and \`prHead\`, and run
1175
+ none. Omit \`diffPath\`, \`diffLines\`, \`messagesPath\`, \`refHead\` and \`prHead\`, and run
1113
1176
  nothing else against the PR yourself.
1114
1177
 
1115
1178
  Report \`diffPath\` = the SNAPSHOT_RUN_ROOT value with '/pr.diff' appended, ONLY
1116
- if 'gh pr diff' exited 0. Report \`refHead\` = the sha the 'echo "$ref"' line
1179
+ if 'gh pr diff' exited 0. Report \`messagesPath\` = the SNAPSHOT_RUN_ROOT value
1180
+ with '/pr-messages.md' appended, ONLY if the block printed no
1181
+ SNAPSHOT_MESSAGES_FAILED line: the block prints it, and removes the file, when
1182
+ the 'gh pr view' that writes it fails — a failed read would otherwise leave an
1183
+ empty file that reads as a clean body, and nothing else the block prints shows
1184
+ that read's exit status. The file holds the PR body and the message of each
1185
+ commit gh lists — at most the first 100 — as they stand now, and the block
1186
+ writes it once — never re-read or rewrite it.
1187
+ Report \`refHead\` = the sha the 'echo "$ref"' line
1117
1188
  printed, \`prHead\` = the headRefOid and \`diffLines\` = the wc -l count. Which
1118
1189
  read answers is chosen by \`isCrossRepository\`, not by whether the first read
1119
1190
  comes back empty. A same-repo PR's branch cannot collide with anything else on
@@ -1168,7 +1239,7 @@ they disagree, so a reconstructed path costs the run rather than sending six
1168
1239
  specialists into another run's tree. Report the HEAD sha, and — in
1169
1240
  \`diffStats\` — the SINGLE-LINE JSON object diff-stats.mjs prints to
1170
1241
  STDOUT, copied verbatim. Only runRoot, path, head, pathVerified and repoVerified
1171
- are ever required — diffStats, diffPath, diffLines, refHead and prHead are each
1242
+ are ever required — diffStats, diffPath, diffLines, messagesPath, refHead and prHead are each
1172
1243
  omitted independently when their command failed, repoError only accompanies
1173
1244
  a false repoVerified, repoUnresolved only accompanies SNAPSHOT_REPO_UNRESOLVED,
1174
1245
  installError only replaces installCmd and installExit, and uncoveredError only
@@ -1340,7 +1411,7 @@ chains its own \`cd\` into the snapshot or into your scratch dir, both named
1340
1411
  in this prompt as absolute paths.
1341
1412
 
1342
1413
  ${readRules(usableDiff(snap), stats, snap)}
1343
-
1414
+ ${d.key === "comments" ? `\n${messagesNote(usableMessages(snap))}\n` : ""}
1344
1415
  ${sharedRunNote(sharedRun, failureOwner, d.key)}
1345
1416
 
1346
1417
  ${environmentNote(snap)}
@@ -1459,7 +1530,7 @@ is set to a corrupted value, while zsh behaves exactly as with the bare
1459
1530
  \`$cfg\` — \`BADJ\` never set, exit 0.
1460
1531
 
1461
1532
  ${readRules(usableDiff(snap), stats, snap)}
1462
-
1533
+ ${d.key === "comments" ? `\n${refuterMessagesNote(usableMessages(snap))}\n` : ""}
1463
1534
  ${environmentNote(snap)}
1464
1535
 
1465
1536
  Lens ${i + 1}: ${i === 0 ? "is the claim true of the code as merged?" : "is it already handled elsewhere, or does the evidence prove something weaker than the claim?"}