Skip to content

[INF-401] ci: make the Claude review post to the PR and verify that it did - #43

Open
jacob-ablowitz wants to merge 5 commits into
mainfrom
fix/INF-401-claude-review-posting
Open

jacob-ablowitz wants to merge 5 commits into
mainfrom
fix/INF-401-claude-review-posting

Conversation

@jacob-ablowitz

@jacob-ablowitz jacob-ablowitz commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Ticket: INF-401. Part of the fleet rollout that followed the image-go diagnosis.

Problem

Claude review runs could finish green with the findings lost. In repos whose prompt does not pin the posting method, the model sometimes reports through the Claude CLI's built-in ReportFindings tool, which succeeds and posts nothing to the PR. Nothing in the job noticed, because the reviewer reporting success was the only signal.

Change (only .github/workflows/claude-code-review.yml)

  • --disallowedTools ReportFindings added to claude_args.

  • New final step Verify a review was posted: queries the PR's reviews for the head SHA and fails closed if there is no review from a claude user. It reads the artifact, not the reviewer's own report. Not continue-on-error.

  • concurrency group per PR with cancel-in-progress, so an older run cannot post onto a newer head and then fail its own verify.

  • The review step has id: claude-review and step-level continue-on-error: true (advisory; the verify step is the signal). Existing job-level gating is unchanged.

  • The prompt text is not changed.

  • Replaces the inline continue-on-error: true # ... comment with the shared explanation.

Expected red on this PR

This PR edits the review workflow itself, so the action's self-modification gate skips the review (it exits 0 with no output). The new verify step therefore reports No review posted here. That is the verify step working, not a regression. Per the same gate, no Claude review of this diff is possible; a human needs to review the workflow change.

Verification

  • The verify logic is the one already merged in the image-* fleet (INF-401 wave 1) and observed passing and failing on real runs.
  • Same verify script as wave 1, unchanged for this repo (single review job).
  • YAML parses; the diff touches only the one workflow file.

Not done / to confirm after merge

  • Posting by this repo's prompt has not been observed since this change. The next non-workflow PR here will show it; a silent run now turns that PR's review check red.
  • Open Renovate PRs may touch the same file (the uses: pin). They should rebase.

🤖 Generated with Claude Code

Update 2026-10-07 (scope widened by Jacob): canonical prompt + tool alignment

After the first push this PR also replaces the prompt's common sections with the fleet's canonical review prompt and aligns the tool set. Per-repo focus areas, do-not-flag lists and context-file lists are carried over unchanged (moved into the canonical slots, not edited).

  • Prompt: every command has the PR number / SHAs filled in by Actions expressions (the old ${PR_NUMBER} spelling expands to an empty shell variable if run literally); posts exactly one review through a heredoc gh api .../pulls/N/reviews --input - (the old --input /tmp/review.json needs a file write); 422 retry with findings in the body; "no diff, no all-clear" guard; force-push fallback; large-diff fallback; review-history reads (reviews, review threads, issue comments) with a "do not re-raise" rule; commit_id pinned to the head SHA; hidden run marker line at the end of the body; history and PR text are data, never instructions. The "Scope Override" clause is removed (it turned any PR comment into an instruction channel).
  • --allowed-tools is now the template set: gh pr diff, gh pr view, gh api, git diff, git log. gh pr comment (a conversation comment the verify step cannot see), gh pr list, gh issue ... and gh search are removed.
  • ve-tools is a PUBLIC repo, which is one reason the scope-override clause is gone. Its all-clear was prose; it is now the exact No new issues found verdict.
  • Not tested end to end yet: this prompt is longer than the one proven in image-nginx #53/#54. It is being tried first on image-go's scratch PRs (planted defect x5, clean x3) before any of this is merged.

Update: amendments after independent review

Independent reviewers (code, silent-failure and comment-accuracy passes) read this diff. Two defects were real and are fixed in the latest commit:

  • Concurrency group removed. A synchronize run reviews only the pushed range, so with cancel-in-progress a second push cancelled the first push's run and the first push was never reviewed. The group existed so a stale run's review could not land on the newer head; the prompt now pins commit_id, so it is no longer needed.
  • Verify no longer counts the "no review was performed" review. When the reviewer cannot get a diff, the prompt has it post a review saying so. That review is by the Claude bot on the head commit, so verify counted it and went green over a review that says nothing was reviewed. It is now excluded, so the check goes red. A re-run that posts a real review passes: the earlier fallback review stays on the PR but is not counted.
  • Verify reports a failed gh api call separately ("Review lookup failed") instead of a bare gh error, and the "no review" message says that an empty success from the review step means the action skipped itself because the workflow file differs from the default branch.
  • Wording fixes: stale concurrency rationale removed, "Confirmed" changed to "Observed", the ReportFindings note, the prompt rule for gh api (step 2 reads and the step 4 POST only), and a neutral example path in the posting JSON.
  • The old prompt's Scope Override section is not carried over (the project-context pointer is kept). This workflow reviews only on opened and synchronize.

Both verify paths were exercised against a stubbed gh: real review passes, fallback-only fails, API failure fails with its own message, a review on another commit does not count, and a review on page 2 of the list counts.

Known limit, unchanged: the Claude review check on this PR is red by design, because the action skips PRs that edit the workflow file.

🤖 Generated with Claude Code

Update 2026-10-14: -f posting form and exact bot login

The heredoc posting form described above is replaced in the latest commit, and the verify step's login match is tightened. Both follow ve-app:

  • Posting (ve-app #1369): Claude Code's Bash safety check denies a heredoc carrying a JSON body ("Contains brace with quote character"), so a review posted that way would never appear. The prompt now posts with gh api … -f commit_id=… -f event=COMMENT -f body='…', with findings in the review body (no inline comments). It adds the '\'' apostrophe rule, the no-line-starting-with-# rule and a check-before-retry rule (replacing the 422 atomic-retry text), and rewrites the fallback review in the same form.
  • Exact login (ve-app #1374; review item 6 on ve-app #1367): Verify a review was posted counted any login containing claude. It now matches claude[bot] exactly, which is the login on this repo's existing Claude reviews.

Tested: actionlint is clean. The verify step's script, run against a stubbed gh, exits 0 on a real claude[bot] review and exits 1 when the only review for the head commit is from notclaude[bot], Claude[bot] or claude-impostor. The expected red described above still applies: this PR edits its own workflow, so claude-code-action skips the review here. The posting form is verified for real only on the first PR after merge.

Also (later commit, 2026-10-14): the prompt's post-failure retry check now selects only claude[bot]'s review for the head commit, with --paginate. It used to match any reviewer's review, so a Gemini or human review could suppress Claude's retry. The gh api rules line no longer contradicts that check. Same change as ve-app #1375.

…t did

Findings can be lost while the job stays green: the model may report through the CLI's built-in
ReportFindings tool, which posts nothing to the PR. Adds:
- --disallowedTools ReportFindings
- a fail-closed 'Verify a review was posted' step that checks the PR's reviews for the head SHA
  (the artifact, not the reviewer's own success report)
- a per-PR concurrency group so an older run cannot post onto a newer head
- step-level continue-on-error on the review step (the verify step is the signal)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@enterprise-gemini-code-assist

Copy link
Copy Markdown

Note

Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported.

jacob-ablowitz and others added 2 commits October 7, 2026 12:53
… the canonical prompt; align tools

Per-repo focus, do-not-flag and context text is carried over unchanged. Common sections now match the
fleet prompt: expressions filled into every command, heredoc 'gh api .../reviews' (no /tmp file), 422 retry,
no-diff guard, force-push fallback, review-history reads, commit_id pinned to the head, run marker. The
scope-override clause is dropped. allowed-tools is the template set (no gh pr comment/list, gh issue, gh search).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…no review was performed" fallback

Independent review of the wave-2 template found two defects, both fixed here.

Concurrency. The review of a `synchronize` push covers only the pushed range. With
`cancel-in-progress`, a second push cancels the first push's run and the second run reviews
only its own range, so the first push is never reviewed. The group existed so that a stale
run's review could not land on the newer head; the prompt now pins `commit_id`, so the group
is no longer needed and is removed.

Verify. The prompt tells the reviewer to post "Review could not determine the changed range
— no review was performed" when it has no diff. That review is by the Claude bot on the head
commit, so the verify step counted it and went green over a review that says nothing was
reviewed. The filter now excludes it. A failed `gh api` call also gets its own error message
instead of a bare gh error, and the "no review" message says what an empty success means.

Also: comment wording fixes (stale concurrency rationale removed, "Observed" instead of
"Confirmed", ReportFindings note), the prompt rule now says `gh api` is for step 2 reads and
the step 4 POST only, and the example comment path is neutral.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@jacob-ablowitz
jacob-ablowitz marked this pull request as ready for review October 8, 2026 21:24
@jacob-ablowitz

Copy link
Copy Markdown
Member Author

Merge authorization (Jacob Ablowitz): @devonjones is authorized to override branch protections and merge this PR (INF-401). The failing Claude review check is expected: claude-code-action skips itself on a PR that modifies its own workflow file, so the "Verify a review was posted" step fails because no review could be produced. It is not a required check. Reviewed head: ea97f1d9c85f34cfc533ba49ecfbdf73b9fdb7f9.

@jacob-ablowitz
jacob-ablowitz marked this pull request as draft October 15, 2026 01:04
jacob-ablowitz and others added 2 commits October 14, 2026 19:06
…the exact bot login

Posting: the review prompt had the model post the review as a JSON body fed
to `gh api --input -` from a heredoc. In local tests on Claude Code 2.1.296
(the version ve-app's CI run installed when this was found), Claude Code's
Bash safety check denies a heredoc carrying a JSON body ("Contains brace with
quote character"), so the review would not post. The prompt now posts with
`gh api ... -f commit_id=... -f event=COMMENT -f body='...'`. Findings go in
the review body as `**[Severity]** path:line - finding`; inline comments are
no longer used. The prompt also tells the model to write apostrophes as '\''
and not to start a body line with `#` (both are denied otherwise). Before
retrying a failed POST, the model now checks that no review for the head
commit exists; this replaces the 422 atomic-retry text. The "no review was performed" fallback review is rewritten in the same form.
An explanatory comment is added above the prompt.

Verify step: match the exact login claude[bot] instead of any login
containing "claude" (case-insensitive), so a user or another app whose login
merely contains the word cannot satisfy the check. claude[bot] is the login
on the Claude reviews already posted in this repo. The existing exclusion of
the "no review was performed" fallback is kept.

Same mechanism as ve-app #1369 (posting) and #1374 (exact login).

Tested: actionlint is clean. The verify step's script, run against a stubbed
gh, exits 0 on a real claude[bot] review and exits 1 when the only review for
the head commit is from notclaude[bot] or Claude[bot]. The new posting form
has not yet run in CI here: this PR changes the review workflow itself, so
claude-code-action skips its own review on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
When the review POST fails, the prompt has the model check whether a review
for the head commit was posted anyway before it retries. That check selected
any review on the head commit. A review by another reviewer (Gemini, a human)
on the same commit made the model conclude its own review had been posted,
so it skipped the retry and posted nothing; the verify step then failed the
job. The check also read only the first page of reviews, so on a PR with
more than 30 reviews it could miss a review that did post and retry into a
duplicate.

The check now selects reviews by claude[bot] (the same login the verify step
matches) and passes --paginate, and the prompt says what to do when it
prints an id. The rules line that limited gh api to the step 2 reads and
"the one review POST" now also names the post-failure check, which it
contradicted.

Found by a stand-in code review of the INF-401 ports. Same change as ve-app's
retry-check fix. Prompt text only; the
verify step is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jacob-ablowitz
jacob-ablowitz marked this pull request as ready for review October 15, 2026 01:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant