Repository navigation
feat(address-review): answer every final-gate thread before pushing in first-line mode (G3) - #120
Conversation
…n first-line mode (G3) In first-line mode, address-review replies on each unresolved final-reviewer thread (Fixed in <sha>, or Disputed:/Declined:/Deferred: with an issue ref) before it pushes, and never posts the re-review command. Fixture tests extract the skill's jq blocks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNDeh9EQwaMjc7xZXRRees
| | defer it | `Deferred:` and an issue reference (`#N`, `owner/repo#N` or an `/issues/N` URL). File the issue first, as in [Replies that defer must name an issue](#replies-that-defer-must-name-an-issue); that section's gate is mandatory here whatever `createPr.requireIssueForDeferredWork` says. | | ||
|
|
||
| A `Fixed in` reply is not an answer on its own: the code has to show the fix. Commit the fix, post | ||
| the replies, then push (this reverses Phase 5's push-then-reply order for these threads, so the | ||
| replies are already on the PR when the first-line reviewer looks at the new head). Do not resolve | ||
| the threads. Never post `review.rereviewCommand` or any `@`-mention of the final reviewer; the |
There was a problem hiding this comment.
P2 · VR-829f0b6c · A Fixed-in reply can name a sha that is not on the PR and still clear the gate
The final-gate filter accepts any hex sha after 'Fixed in', so a reply naming a commit that never reached the PR still clears the gate. The check that the commit is on the pushed head exists only as an instruction.
Reply Fixed in <sha> or Disputed: with a reason.
Details for agents: evidence, failure scenario, verification, proving test
Step 4 requires each 'Fixed in <sha>' reply to use a short sha of a commit on the pushed head, checked with git merge-base --is-ancestor, because an amend or rebase changes the sha. That check is prose only. The mechanical gate, the thread filter (SKILL.md ~line 440-455), accepts any 7-40 hex string after 'Fixed in' (test("^Fixed in [0-9a-f]{7,40}\\b")) and never looks at git or the PR head. Step 5 then says the filter 'must print []' and treats that as the gate passing.
Evidence: SKILL.md:393-399 (step 4: merge-base check is only prose; 'never from memory'). SKILL.md filter 'answer' definition (~line 445-446) uses only the regex. scripts/test-address-review-first-line.sh:63 only greps for the prose 'merge-base --is-ancestor <sha> "$NEW_HEAD"', not for an enforced check. No git call appears in the filter or reply-check blocks.
Re-review (fix-regressed): a finding VR confirmed fixed is back in the change.
Failure scenario
The agent commits a fix, amends it after pre-flight (changing the sha), pushes, and replies 'Fixed in <pre-amend sha>' on three threads. The pre-amend sha is not an ancestor of the pushed head. The filter prints [] because the reply matches the regex, so the agent reports the answer gate cleared. The final reviewer and the first-line reviewer read 'Fixed in <sha>' pointing at a commit that is not on the PR, and the thread is closed on a claim of work that the head does not contain.
How this was verified (read-only-reasoning)
The filter regex accepts any 7-40 hex sha after 'Fixed in'. The is-ancestor check exists only in step 4 prose. Reachable from: address-review first-line step 4-5.
Proving test
Fixture: a final-bot thread whose only later non-bot comment is 'Fixed in deadbee1' where deadbee1 is not an ancestor of the head. Run the thread filter from SKILL.md with a head that does not contain deadbee1; assert the thread is listed (the reply is not an acceptable answer for a sha not on head). On the PR head the filter prints [] and the test fails. The filter has no git input, so the check needs either an ancestor test in the gate or a rule the test can verify.
lens spec-adversary · symbol Around every push: answer the final reviewer's threads
There was a problem hiding this comment.
Fixed in 40f5fac. Fixed in replies now go up only after a push succeeds, and they are posted straight away. The sha is taken from the pushed history and checked with git merge-base --is-ancestor <sha> "$NEW_HEAD". If the push fails, no Fixed in reply is posted. The first-line push cooldown covers the gap between the push and the replies. Disputed, Declined and Deferred replies name no commit, so they can still go up at any point. The test now asserts both lines.
Generated by Claude Code
There was a problem hiding this comment.
Fixed: confirmed @ 244e7ec. Thread resolved.
There was a problem hiding this comment.
still open @ 39d4912 (fix-regressed)
There was a problem hiding this comment.
Declined: the filter decides whether a thread has an acceptable answer. Whether the cited sha is on the head is checked before the reply is posted, in step 4 with git merge-base --is-ancestor <sha> "$NEW_HEAD". Putting git into the jq filter would make it depend on a checkout it does not have. The first-line reviewer also confirms a fix from the hunk, not from the reply text (Fixed in with no fixing hunk stays open), so a wrong sha cannot close a finding.
Generated by Claude Code
Vycari ReviewRound 5 on 6e7a475. The verdict is the Vycari Review check run, and each open finding is an inline thread. Reply on every P0 and P1 thread: Open: P0 0 · P1 0 · P2 2 · disputed 0 · confirmed fixed 1 · withdrawn 1 Hand-off: Greptile requested on 6e7a475. Greptile follow-through
No open P0 or P1 findings this round. Outside the change since
|
…, count only answers Answers Vycari Review on maintainerd#120: - VR-829f0b6c: Fixed in replies go up straight after a successful push, on a sha checked with merge-base --is-ancestor against the pushed head; the first-line push cooldown covers the gap. Disputed/Declined/Deferred replies, which name no commit, may go up any time. - VR-00c7ed39: the skill now spells out the GraphQL reviewThreads read (isResolved, databaseId, body, author) that the filter consumes. - VR-6fef581d: a thread leaves the owing list only when a non-bot reply passes the reply check; a question or a thanks does not count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNDeh9EQwaMjc7xZXRRees
… latest comment Answers VR-cd1164e6 on maintainerd#120: a final-reviewer follow-up on a thread the agent already answered puts the thread back on the owing list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNDeh9EQwaMjc7xZXRRees
|
|
||
| Do not resolve the threads. Never post `review.rereviewCommand` or any `@`-mention of the final | ||
| reviewer; the first-line reviewer requests it when it is happy. | ||
|
|
||
| Phase 1's REST reads do not group comments into threads or say whether a thread is resolved, so |
There was a problem hiding this comment.
P2 · VR-5f6f903c · Any non-bot account's reply satisfies the gate, so a third party's 'Declined:' or 'Fixed in' silences an owed answer
Any account that is not a bot can clear a final-reviewer thread from the owed list by posting a reply that starts with an accepted prefix. A passer-by's reply can therefore stand in for the PR author's own answer.
Reply Fixed in <sha> or Disputed: with a reason.
Details for agents: evidence, failure scenario, verification, proving test
The filter's 'answer' test accepts a reply from anyone outside $bots (the code selects 'from anyone outside $bots'), not from the PR author or a maintainer. A drive-by commenter's text starting 'Declined:' or 'Fixed in <7+ hex>' on a final-reviewer thread therefore removes that thread from the list the agent must answer. The rule says the agent must give each such thread one of its own replies, and the step-5 gate is meant to catch exactly the threads still owing one.
Evidence: SKILL.md filter: 'select(by($bots) | not)' then 'answer' test; prose: 'from anyone outside $bots'. The test script case 9 and case 2 use 'alice' as the human answerer, showing the intent is any non-bot. Did not find an author-identity check in the diff or in the section. The first-line reviewer's hold check is not in this diff, so whether it independently requires an author reply is unverified.
Failure scenario
A P1 final-reviewer thread on src/auth.go is unanswered by the PR author. A third-party user replies 'Declined: intentional'. The agent's filter excludes the thread, prints [] and pushes without answering. The first-line reviewer and the final reviewer see a P1 thread with no author reply, while the agent reported the answer gate cleared.
How this was verified (read-only-reasoning)
I read the filter in the diff. It excludes only $bots authors and does not check the PR author. I did not open any code that shows how the reviewer's hold check works. Reachable from: address-review first-line mode, thread filter.
Proving test
Thread fixture: final-bot opens a thread, a non-bot user 'outsider' replies 'Declined: intentional', no author reply. Run the thread filter with bots=[final-bot]: the expected output lists the thread's databaseId because the PR author has not answered; the current filter returns [] and the assertion fails on the head.
lens spec-adversary
There was a problem hiding this comment.
Declined: the agent replies under whichever maintainer's login it runs as, and that is often not the PR author. Examples are foreman or auto-dev PRs opened by an App, or a teammate driving someone else's PR. An author-only rule would hold those threads forever. The merge-side thread check (ws prs blockOnOpenThreads) also counts any non-bot reply as answered, so this keeps the two gates consistent. A drive-by Declined: from an outsider is visible on the thread for the maintainer, who merges.
Generated by Claude Code
The thread read fetched only the first 100 review threads, so an unanswered final-reviewer thread past that page was never checked (VR-3b391fe0). The read now follows $endCursor with --paginate and joins the pages, a failed read stops the step instead of reading as [], and a thread whose comments run past the first page is listed because its latest replies were never read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNDeh9EQwaMjc7xZXRRees
comments(first: 100) returned a long thread's oldest comments, so a reply the agent posted there was never read and the thread stayed listed (VR-1b9c9dbd). The read now takes the opener (the id to reply to) and the latest 100 comments, and the filter judges the answer on those. Step 4 names review.pushCooldownMinutes, the window the Fixed in replies are posted in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNDeh9EQwaMjc7xZXRRees
…fore it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNDeh9EQwaMjc7xZXRRees
|
@greptileai review |
…ine mode (VR-ce983ad9) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QNDeh9EQwaMjc7xZXRRees
|
@greptileai review |
Requested by Allen · project thread
Human overview
In first-line mode,
address-reviewnow answers every unresolved final-reviewer thread around each push:Fixed in <sha>.Declined:, orDeferred:with an issue reference.This matches the first-line reviewer's new rule: it holds the hand-off until every final-reviewer finding is fixed and answered. This is phase G3 of
docs/design/vr-greptile-followthrough.mdin Vycari/vycari (Refs Vycari/vycari#187).AI reviewer
What changed
plugins/repo-ops/skills/address-review/SKILL.md, First-line mode, new subsection "Around every push: answer the final reviewer's threads" (before Stage 2):review.botslogin:Fixed in <sha>,review.firstLine.disputePrefix(defaultDisputed:),Declined:, orDeferred:with#N/owner/repo#N//issues/N. The issue is filed first.review.pushCooldownMinuteswindow, it posts eachFixed in <sha>. The sha is checked withgit merge-base --is-ancestoragainst the pushed head. If the push fails, noFixed inis posted.[].reviewThreads(--paginate,$endCursor), joins the pages, and stops on a failed read. For each thread it takes the opener (the id to reply to) and the latest 100 comments.review.rereviewCommandor any @-mention of the final reviewer.@greptileai please re-reviewinstruction is now gated "not in first-line mode". Non-first-line text is otherwise unchanged.plugins/core/references/config-schema.md: one sentence, synced into the six vendored copies with./scripts/sync-references.sh.scripts/test-address-review-first-line.sh(new, 23 checks), wired into.github/workflows/validate.yml. It extracts both jq blocks from SKILL.md and runs them on fixtures, including long threads whose opener is outside the latest-100 window. It also checks the order, pagination and failed-read lines.Declined: VR-5f6f903c. An author-only answer rule would hold threads answered by a maintainer driving someone else's PR.
ws prsblockOnOpenThreadsalso counts any non-bot reply.Limits. The tests check the filters and key phrases. They do not check an agent actually running the skill.
Verification (actual results)
./scripts/test-address-review-first-line.sh: 23 passed, 0 failed (a mutation of the// -1fallback fails it)./scripts/test-repo-ops-hooks.sh,test-wait-tools.sh,test-coverage.sh,test-profile.shand the rest ofscripts/test-*.sh: pass./scripts/sync-references.sh --check: all vendored references in syncpython3 scripts/check-links.py: exit 0Checklist (repo-specific)
plugin.jsonormarketplace.jsonchangedaddress-review/SKILL.mdchanged./scripts/test-coverage.shand./scripts/test-profile.shpass locally./scripts/sync-references.sh --checkpassespython3 scripts/check-links.pypasses🤖 Generated with Claude Code
https://claude.ai/code/session_01QNDeh9EQwaMjc7xZXRRees


Confidence Score: 5/5
The PR appears safe to merge; no new actionable issues were found.Summary
address-reviewnow requires answers on unresolved final-reviewer threads in first-line mode.Reviews (2) · Last reviewed commit: "address-review: gate the Phase 6 stale-s..." · Reviewed by Greptile