You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Read the Reviewer's Node Id Repo-Wide Instead of From the Pull Request (#628)
**Disposition** `Amends "Re-vendor the changed \`verbatim\` content,
which is one sweep covering seven files."` under "Fleet Sweeps". No
cluster under "Work Clusters" covered this, and a cluster created and
deleted in the same change records nothing, so the observation lands on
the entry that already reasons about what each downstream copy owes.
No closing keyword, since a feature to `develop` pull request never
fires one.
## What was wrong
"Triggering and Polling" in the runbook said the reviewer bot's node id
is read from an existing Copilot review **on the pull request being
reviewed**. Three statements asserted it:
- The round-1 paragraph: "The `requestReviews` mutation below is for
**re-requesting on later pushes** (a new head SHA). By then a prior
review exists, so its bot node id is readable."
- The step-1 code comment, whose query was `pullRequest(number: <N>) {
reviews }`.
- The paragraph after the recipe: "step 1 needs at least one prior
formal review on the PR".
A "Cold start" paragraph added later says the opposite in the same
section, that the id is the reviewer account's and is stable across
every pull request in the repo. The three statements above were never
swept, so the section contradicted itself and the strict reading is the
one a reader hits first. Acting on it, a pull request that
auto-review-on-open never fired on has no way to reach round 1 and gets
handed to the maintainer to seed through the UI, which is the hand-off
the mutation exists to remove.
## What the tree says
Read across the newest eight pull requests in this repo on 2026-08-08,
every Copilot review carries the same bot node id. It is an account id,
so nothing has to land on a pull request before it can be read.
## The change
- The round-1 paragraph drops the two claims above and says a round 1
carrying no review means wait, then request it yourself.
- Step 1 queries the 20 newest pull requests with the `first // empty`
guard and the `[ -z ]` stop, so it resolves on a pull request with no
review of its own.
- The per-pull-request claim and the cold-start exception collapse into
one statement of the id's scope, which drops the duplicated query block.
The pull-request-scoped read stays as a note that it buys nothing over
the repo-wide read and fails on exactly the round the repo-wide read
handles.
- `TODO.md` records the re-vendor. This propagates a procedure rather
than refreshing a hash, so a downstream repo left on the old copy is
wrong on the rule. The
[#578](#578) bullet
claimed to be the sole rule-propagating half of that sweep, which a
second one falsifies, so it is widened to two.
## Verification
Both corrected blocks were run verbatim against this repo before the
prose was written, reads only, and both returned the id.
`scripts/prose_lint.py` exit 0, `markdownlint-cli2` 0 issues,
`editorconfig-checker` exit 0, and both files were checked line by line
for CRLF, `.github/copilot-instructions.md` at 353 of 353 and `TODO.md`
at 489 of 489.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
**Round 1 is normally auto-seeded, so poll for it before trying to self-trigger.** Auto-review-on-open supplies the first review with no `botIds` call needed, but it can lag one to three minutes. After opening a PR (or the first push), **poll** for a Copilot review on the head SHA (see [Verify Review Covered Current Head](#verify-review-covered-current-head)) before concluding none ran. The `requestReviews` mutation below is for **re-requesting on later pushes** (a new head SHA). By then a prior review exists, so its bot node id is readable. A missing bot node id on round 1 therefore means "the auto-review has not landed yet - wait and poll," **not** "ask the maintainer to kick it off."
73
+
**Round 1 is normally auto-seeded, so poll for it before trying to self-trigger.** Auto-review-on-open supplies the first review with no `botIds` call needed, but it can lag one to three minutes, and on some pull requests it never fires at all. After opening a PR (or the first push), **poll** for a Copilot review on the head SHA (see [Verify Review Covered Current Head](#verify-review-covered-current-head)) before concluding none ran. Where it never lands, drive round 1 with the same `requestReviews` mutation every later round uses, which needs nothing this PR has to produce first. A round 1 carrying no review therefore means "wait, then request it yourself," **not** "ask the maintainer to kick it off."
74
74
75
75
> **The reviewer login differs by API, in three forms rather than two.** In **GraphQL** (`gh api graphql` and `gh pr view --json reviews`, which is GraphQL-backed) the `Bot.login` is `copilot-pull-request-reviewer`, with **no `[bot]` suffix**. In the **REST** API (`gh api repos/.../issues|pulls/...`) the same account's `user.login` is `copilot-pull-request-reviewer[bot]`, **with** the suffix. In a REST **timeline**`review_requested` event the `requested_reviewer` is a third spelling again, login `Copilot` with `type``Bot`, so a filter written against either of the other two selects nothing there and reports a pull request with requests as having none. Match on the type plus a loose login test rather than on any one spelling, and each query below uses the correct form for its API.
76
76
77
77
```sh
78
-
# 1. PR node id + the Copilot reviewer's bot node id (read from any existing
79
-
# Copilot review; the reviewer login is `copilot-pull-request-reviewer`).
78
+
# 1. PR node id, plus the reviewer bot's node id read across the repo's recent PRs.
79
+
# The bot id is the reviewer account's own, so every PR in the repo carries the same one.
80
+
# The reviewer login is `copilot-pull-request-reviewer` in GraphQL.
80
81
PR_NODE=$(gh pr view <N> --json id --jq '.id')
81
82
BOT_ID=$(gh api graphql -f query='
82
83
{
83
84
repository(owner: "<owner>", name: "<repo>") {
84
-
pullRequest(number: <N>) {
85
-
reviews(first: 50) { nodes { author { __typename login ... on Bot { id } } } }
The bot node id is read from an existing Copilot **formal** review (`pullRequest.reviews`), so step 1 needs at least one prior formal review on the PR, and the auto-review-on-open normally supplies the first one (it may have **no inline comments**, which still counts, and its bot node id is still readable). Poll for it (give auto-review-on-open a few minutes) before deciding it is missing.
102
-
103
-
**Cold start (round 1 not yet landed): read the id repo-wide, not from this PR.** The Copilot reviewer's bot node id is the reviewer bot *account's* node id and is **stable across every PR in the repo**. So a freshly opened PR that has neither a formal review nor an issue comment yet does **not** need UI seeding to bootstrap the id: read it from any prior Copilot review anywhere in the repo, then feed it into the `requestReviews` mutation to drive round 1. Query the **most recent** PRs (`first: 20` with an explicit newest-first order, since plain `last: 20` returns the *oldest* PRs, which may predate Copilot on the repo), and **guard for an empty result**, since an empty `$BOT_ID` means none of the sampled PRs carry a Copilot review. Widen the window (raise the count or paginate) before concluding the repo has never had one and falling back to UI seeding. Never feed an empty id into the mutation:
echo"no Copilot review in the 20 most recent PRs - widen the window, else fall back to UI seeding">&2
118
-
return 1 2>/dev/null ||exit 1 # stop; do NOT call requestReviews with an empty id
119
-
fi
120
-
```
106
+
**The bot node id belongs to the reviewer account, not to a pull request**, and it is the same id on **every PR in the repo**, so nothing has to land on this PR before step 1 can read it. A PR opened a minute ago, with no review and no comment of its own, needs no UI seeding to bootstrap the id and no prior review to source it from: any Copilot review anywhere in the repo carries it. Query the **most recent** PRs, since a plain `last: 20` returns the *oldest* ones, which may predate Copilot on the repo. **Guard for an empty result**, because an empty `$BOT_ID` says only that none of the PRs sampled carry a Copilot review, so widen the window (raise the count or paginate) before concluding the repo has never had one. Never pass an empty id to the mutation.
121
107
122
-
If Copilot posted **only an issue comment** on this PR and no formal review, you can instead read the id from that comment's author (`pullRequest.comments` -> author `... on Bot { id }`). Manual UI seeding is the last resort, needed only for a repo that has **never** had a Copilot review, so no prior id exists anywhere to read. Use the mutation for every subsequent re-request.
108
+
A read scoped to this PR (`pullRequest(number: <N>) { reviews }`) returns the same id once a review has landed here, and it buys nothing over the repo-wide read while failing on exactly the round the repo-wide read handles. Where the repo's only Copilot artifact is an issue comment rather than a formal review, read the id from that comment's author instead (`pullRequest.comments` -> author `... on Bot { id }`). Manual UI seeding is the last resort, needed only for a repo that has **never** had a Copilot review, so no prior id exists anywhere to read.
123
109
124
110
**Do NOT post `@Copilot review` as a PR comment.** That comment triggers the Copilot *coding agent* (`copilot-swe-agent[bot]`), which makes code changes rather than posting a review.
125
111
@@ -355,6 +341,11 @@ After the final push, sweep-resolve stale older threads for removed code paths.
355
341
-**Proved against** - the 25 most recent merged pull requests as of `develop` at `756a53e`, the corpus on which the anchored verb form that ships instead raises one reference, and that one true.
356
342
-**Delete when** - `claims` stops reading a description for commit references.
357
343
344
+
-**The GraphQL `pullRequests` connection defaults to `states: [OPEN]`, so the bot node id query in "Triggering and Polling" returns nothing in a repository whose Copilot-reviewed pull requests have all merged.** Raised against the repo-wide read, on the ground that a cold start is exactly the case where no open pull request carries a review.
345
+
-**Disproved by** - running the connection both ways against this repository while exactly one pull request was open. With `states` omitted, `pullRequests(first: 5, orderBy: { field: CREATED_AT, direction: DESC })` answers `628 OPEN`, `627 MERGED`, `626 MERGED`, `625 MERGED` and `624 MERGED`, so the omitted default is every state rather than `OPEN`. The same call with `states: [OPEN]` answers `628 OPEN` alone, which is the behavior the finding predicts for the first form and is what distinguishes them. The read was first run when this repository had no open pull request at all, and it returned the id from merged ones.
346
+
-**Proved against** - the `BOT_ID` query in "Triggering and Polling" in this file, run against this repository's pull request list on 2026-08-08.
347
+
-**Delete when** - that query names `states` explicitly, or stops reading pull requests to find the id.
348
+
358
349
-**"The agent check branches" in `STANDUP.md` section 0 is a subject-verb disagreement, and should read "The agent checks branches".** Raised as a suppressed finding against a line the change under review only touched as diff context.
359
350
-**Disproved by** - reading the sentence against the snippet it describes. The subject is the noun phrase "the agent check", meaning the check for the signing agent, and "branches" is its verb, which is what the `if [ ... = ssh ]; then ssh-add -L; else gpg --list-secret-keys; fi` line does. The proposed reading needs "branches" as a plural noun, and the paragraph is section 0, before a repository exists, where the alternatives it names are the SSH and GPG forms rather than refs.
360
351
-**Proved against** - the paragraph following the agent snippet in `STANDUP.md` section 0 on `develop` at `676a2bd`, unchanged since `77be3a3`.
Copy file name to clipboardExpand all lines: TODO.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -353,8 +353,9 @@ Regenerate [reports/divergences.md][divergences-report] before using it as the w
353
353
-**Detail** - Two comment lines in [`.markdownlint-cli2.jsonc`][markdownlint] took the same capitalization, and that file is `verbatim` and `whole`, so every downstream copy is byte-mismatched on a config nothing else changed about.
354
354
-**Detail** - [`CODESTYLE.md`][codestyle] is the fifth file, at `intent` rather than `verbatim`, so it reaches the fleet as a rule each repo adopts in its own copy, and the same mixed spelling waits in every downstream tree.
355
355
-**Detail** - [`.github/copilot-instructions.md`][copilot-instructions] is the sixth, also at `intent`, where "Reply and Thread Resolution Workflow" now leads with the hub's reply helper and keeps the hand-run mutations as the cross-owner and unreachable-hub path. A repo taking the old copy is not broken by it, since the mutations it documents still work, so this rides the visit rather than gating it.
356
+
-**Detail** - The same file's "Triggering and Polling" reads the reviewer bot's node id across the repo's newest pull requests rather than from the pull request under review, because the id is the reviewer account's own and is identical on every pull request in the repo, read as one value across all eight of the newest here on 2026-08-08. This is the other part that propagates a procedure rather than refreshing a hash, so a repo left on the old copy reads its own runbook as requiring a review on the pull request before the id can be read, and hands round 1 to the maintainer to seed through the UI whenever auto-review-on-open does not fire, which is the hand-off the mutation exists to remove.
356
357
-**Detail** - The prose batch rewrote punctuation in five `verbatim`[`GOVERNANCE.md`][governance] sections, "Branching Model", "Release Model", "Documentation Style Conventions", "PR Review Etiquette" and "Workflow YAML Conventions", so every downstream copy of those five is byte-mismatched and the audit reports it as stale. No rule changed meaning, so the re-vendor is a hash refresh rather than a propagation, and a repo taking the old copy is correct on the rule while wrong on the bytes.
357
-
-**Detail** - The [#578][issue-578] widening is the one part of this sweep that propagates a rule rather than refreshing a hash, so a repo left on the old copy is wrong on the rule and not merely on the bytes, which makes it the half to carry first. It touches three `verbatim`[`GOVERNANCE.md`][governance] sections, "Branching Model", "Communicating with the User" and "Operational Repositories", and the third of those matters most on the two `operational` repos that can act on it. [`WORKFLOW.md`][workflow] took a cross-reference in the same change and is `intent`, so nothing reports it.
358
+
-**Detail** - The [#578][issue-578] widening is one of the two parts of this sweep that propagate a rule rather than refreshing a hash, the runbook correction above being the other, so a repo left on the old copy is wrong on the rule and not merely on the bytes, which makes the pair the half to carry first. It touches three `verbatim`[`GOVERNANCE.md`][governance] sections, "Branching Model", "Communicating with the User" and "Operational Repositories", and the third of those matters most on the two `operational` repos that can act on it. [`WORKFLOW.md`][workflow] took a cross-reference in the same change and is `intent`, so nothing reports it.
358
359
-**Detail** - [`WORKFLOW.md`][workflow] is the seventh file and `repo-config/README.md` joins [`CODESTYLE.md`][codestyle] and [`.github/copilot-instructions.md`][copilot-instructions] at `intent`, where a punctuation-only edit produces no hash and therefore no audit finding at all. Nothing reports these, which is why they are recorded here rather than left to the run. `HISTORY.md` is `presence` and is each repo's own changelog, so its one fix owes nothing downstream.
359
360
360
361
-**Carry the `Local Verification` heading into every repository's `OPERATIONS.md`.** The heading leads the file and states what verifying a change there requires, naming the part of the repo's contract CI structurally cannot exercise, and a repo whose gates are entirely in CI says that under it rather than omitting it.
0 commit comments