Skip to content

ci: make deep-review label reviews investigate like a prompted session - #347

Merged
allxsmith merged 1 commit into
mainfrom
ci/deep-review-investigate-341
Jul 22, 2026
Merged

allxsmith merged 1 commit into
mainfrom
ci/deep-review-investigate-341

Conversation

@allxsmith

@allxsmith allxsmith commented Jul 22, 2026 •

Copy link
Copy Markdown
Owner

Fixes #341

On PR #340 the deep-review label path (opus, 120 turns) found 0 findings while a prompted @claude session (sonnet, 60 turns) found 5, including a real residual bug fixed in d2ffb51. The gap was what each session was told to do, not the model. This PR closes it in claude-review.yml's prompt plus one new gate step — no new tools, no new write surfaces.

Prompt changes

  • Evidence phase (step 2): read the PR body and every linked issue; when the PR claims to fix a failure, fetch the cited evidence (issue quotes, gh api run/job logs — already allowlisted) and verify the fix against it empirically. Reading the diff is not verification.
  • Residual-risk hunt (step 3): enumerate how the addressed failure class could still occur; refute each with evidence or post it as a finding. The summary review now requires a Residual risk section. (This is the step that would have caught ci: fail AI triage loudly when the session bails before posting (#338) #340's sentinel-count gap.)
  • PR-type-aware lens (step 4): the existing component checklist applies to bulma-ui//docs/ diffs; a new infra lens (guard/permission bypasses, shell+jq edge cases, untrusted-input handling, silent-failure shapes, idempotency/concurrency) applies to .github//.claude//scripts/ diffs.
  • Advisory tier (steps 6–8): 🔵 Advisory for real limitations/trade-offs that don't block merge. Summary-table only — never inline, so advisories can't become fixer work items and spin the AI loop (verified: the loop's gate counts GraphQL reviewThreads, which only inline comments create). Zero-blocking summaries list advisories instead of a bare "No blocking defects found."; the heading becomes <N> blocking · <M> advisory.

Focus steer (new Extract focus steer step)

A triage+ user may pre-post a PR comment starting with deep-review: before applying the label; it's injected into the prompt as a FOCUS block. Security posture, since anyone can comment on a public-repo PR and the text enters the LLM prompt:

  • Each candidate author's live role is re-verified via the collaborators API (same belt-and-braces as the perm step). Deliberate deviation from the issue's literal wording: the step scans newest-first over the newest steer per distinct author and takes the first that verifies triage+, so a later outsider comment can neither inject text nor displace a maintainer's steer. At most 5 distinct authors are role-checked (bounds attacker-forced API calls).
  • Multi-line output uses a random 128-bit heredoc delimiter, so no comment line (literal EOF, key=value, …) can terminate the value early or smuggle step outputs.
  • Fail-soft: API failures degrade to an unfocused review, never a red run (the fallback sits outside the command substitution — under pipefail, jq -s still prints [] when gh dies, so an inner || echo would concatenate both and corrupt the count).
  • Hygiene: prefix stripped, CRLF normalized, 2000-char cap, whitespace-only steers ignored, untrusted bodies never echoed to logs.

Unchanged: opus model, 120 turns, dedupe marker, one-review invariant, allowed_bots, show_full_output debug-only, the is_error gate.

Docs: the steer is mentioned in the AI-development guide's label table and CLAUDE.md's deep-review sentence.

Verification

  • Workflow YAML parses (js-yaml); prettier + check:conformance green on changed files.
  • The step's script (extracted verbatim from the YAML) passed a 24-check local matrix: trusted-steer selection with a newer attacker comment present, heredoc smuggling (EOF/allowed=false/block=/fake-delimiter lines stay inside the value; only block surfaces as an output key), newest-own-steer-wins, 5-author cap behavior, truncation, CRLF, whitespace-only, comments-API failure (exit 0, empty block), per-author permission-API failure fall-through, and a live no-steer run against PR ci: fail AI triage loudly when the session bails before posting (#338) #340.
  • Two bugs found and fixed by that testing: BSD seq 0 -1 counts down (empty candidate list would loop on a Mac dev box; replaced with a while counter) and the fail-soft || placement above. Side finding, documented in a comment: on a public repo the collaborators-permission endpoint returns read for any user rather than 404, which the case statement already handles.

Post-merge (this workflow can't run on its own PR — the action's OIDC exchange requires the file to match main): re-apply deep-review to a PR fixing a documented failure (#340 is the benchmark), with a deep-review: steer comment posted first, and confirm the review (a) cites the linked issue's evidence, (b) contains a Residual risk section, (c) reports advisories in the summary without creating inline threads.

Summary by CodeRabbit

  • New Features

    • Added an optional deep-review focus mechanism via deep-review:-prefixed pull request comments.
    • Improved review output by separating blocking vs advisory findings, including explicit “no blocking findings” and “zero findings” messaging.
    • Added an automated “screenshots at handoff” step when a pull request transitions to human review, posting results in the pull request and retaining them for 30 days.
  • Documentation

    • Updated AI development guidance with the deep-review opt-in details and screenshots workflow instructions.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@allxsmith, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 54 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6916625d-7316-4e90-80e3-74957c5838b1

📥 Commits

Reviewing files that changed from the base of the PR and between 10a8b3b and 0497843.

📒 Files selected for processing (3)
  • .github/workflows/claude-review.yml
  • CLAUDE.md
  • docs/docs/guides/getting-started/ai-development.md

Walkthrough

The deep-review workflow now accepts verified deep-review: focus comments, expands Claude’s evidence and residual-risk instructions, and reports blocking versus advisory findings. Project documentation updates the label flow, autonomous-loop table, and screenshot capture at human handoff.

Changes

Deep-review workflow

Layer / File(s) Summary
Verified focus steer extraction
.github/workflows/claude-review.yml
Recent deep-review: comments are deduplicated, author permissions are verified, and a structured focus block is emitted for eligible runs.
Prompt and summary contract
.github/workflows/claude-review.yml
The prompt adds the focus block and expanded review procedure; final output distinguishes blocking and advisory findings and defines zero-finding behavior.
Deep-review and handoff documentation
CLAUDE.md, docs/docs/guides/getting-started/ai-development.md
Deep-review guidance is updated, the autonomous-loop table is reformatted, and screenshot-at-handoff behavior is documented.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Maintainer
  participant PRComments
  participant CollaboratorsAPI
  participant ClaudeReview
  participant Claude
  Maintainer->>PRComments: Post deep-review focus comment
  ClaudeReview->>PRComments: Fetch and deduplicate comments
  ClaudeReview->>CollaboratorsAPI: Verify comment authors
  CollaboratorsAPI-->>ClaudeReview: Return author permissions
  ClaudeReview->>Claude: Inject focus and review instructions
  Claude-->>ClaudeReview: Return blocking/advisory summary
Loading

Possibly related PRs

Suggested labels: deep-review

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The added screenshots-at-handoff docs subsection is unrelated to #341's deep-review workflow changes. Move the screenshots-at-handoff documentation into a separate PR or explain why it is required for #341.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main workflow change around deep-review behavior.
Description check ✅ Passed It includes a clear summary, related issue, checklist, verification, and context, though some template checkboxes are unfilled.
Linked Issues check ✅ Passed The workflow and docs updates cover the issue's evidence phase, residual-risk hunt, advisory tier, focus steer, and invariant-preserving requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/deep-review-investigate-341

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://84b65f19.bestax.pages.dev

@allxsmith

Copy link
Copy Markdown
Owner Author

@claude perform a deep review

@claude

claude Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Claude finished @allxsmith's task in 5m 10s —— View job


Deep review

Evidence check on the PR's central claim: pulled gh pr view 340 --comments — confirmed. The deep-review label session on #340 posted "0 finding(s)", while the prompted @claude session found 5 issues, one of which (dropped second command on PR runs, fixed in d2ffb51) was a real residual bug the label session missed entirely. This PR's prompt changes (evidence phase, residual-risk hunt, infra lens) are a direct, well-targeted response to that gap, not a cosmetic rewrite.

Verified locally:

  • pnpm exec prettier --check on all 3 changed files → passes (this also confirms the YAML parses, since Prettier must parse it to format-check it).
  • pnpm run check:conformance → all checks green.
  • Traced the new Extract focus steer shell/jq step (.github/workflows/claude-review.yml:154-222) by hand against its own stated invariants: the reverse | reduce dedup correctly yields newest-per-distinct-author in newest-first order (comments API returns oldest-first, so reverse before the reduce is required and present); the fallback || CANDIDATES='[]' sits outside the pipe as claimed, avoiding the jq -s concatenation bug the comment warns about; the block= default + later block<<DELIM heredoc write is a documented "last write wins" GitHub Actions Outputs pattern, correctly used to make the no-steer path a genuine empty string.

🟠 Major · Correctness — docs describe a feature that doesn't exist in this repo

docs/docs/guides/getting-started/ai-development.md:90-99 (new "Screenshots at handoff" section) documents an automated Playwright screenshot pass named story-screenshots.yml, dispatched whenever claude-pr-loop.yml flips a PR to needs-human-review, storing images on a story-screenshots branch.

None of this exists in the repository as of this diff:

  • ls .github/workflows/ has no story-screenshots.yml (verified — full listing checked).
  • Neither needs-human-review transition site in claude-pr-loop.yml (lines ~593, ~775) dispatches any screenshot workflow — both just post a surf-themed comment and add a reviewer.
  • No story-screenshots branch reference, and no Playwright screenshot tooling outside the unrelated visual-regression.yml and create-bestax e2e suite.

A maintainer who follows this doc's own instruction — gh workflow run story-screenshots.yml -f pr=<number> — gets "workflow not found." CodeRabbit's automated pre-merge check already flagged this section as out-of-scope for issue #341; the deeper problem is it's not just out of scope, it ships documentation for an unimplemented feature. Recommend dropping the "Screenshots at handoff" paragraph from this PR (move it to whatever PR actually adds story-screenshots.yml).

Fix this →

🔵 Advisory · Robustness — the 5-distinct-author cap can bury a genuine steer

.github/workflows/claude-review.yml:176-179: candidates are capped to the 5 most-recent distinct authors before any permission check runs. If 5+ distinct GitHub accounts post deep-review:-prefixed comments after a real maintainer's steer (whether adversarial or just a noisy thread), the maintainer's comment falls out of the candidate list entirely and is never role-checked — the review silently degrades to unfocused. This is consistent with the step's own "best-effort, fail-soft" design (no security bypass results, since a dropped steer just means no FOCUS block, same as today), and the realistic window is small since maintainers normally comment-then-immediately-label. Not blocking, but worth being aware of if a deep-review: steer is ever posted well before applying the label on a busy PR.


Overall: The prompt-engineering changes are the well-evidenced core of this PR and hold up — they're a direct, cited fix for the exact gap #341 describes, and the new focus-steer step's trust logic (live role re-verification, newest-per-author dedup, random-delimiter heredoc against injection) is sound on manual trace. The one real defect is scope creep that shipped broken: the "Screenshots at handoff" doc section describes tooling that isn't in this repo.

Residual risk: Beyond the two items above, I didn't find a way the original #340 failure mode (deep-review missing evidence-backed residual bugs) could still occur through this diff — the new steps 2-4 explicitly require fetching cited evidence and enumerating residual-risk variants before judging code, which is exactly the gap that let #340's Finding 1 through.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://128f7906.bestax.pages.dev

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/docs/guides/getting-started/ai-development.md`:
- Around line 90-100: Update the “Screenshots at handoff” section to remove
unsupported claims about story-screenshots.yml, its pr input, artifact
retention, story-screenshots branch, and “nothing to screenshot” behavior unless
corresponding workflow source is checked into the repository; otherwise replace
the details with behavior verifiable from existing checked-in sources.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ea650ed6-1263-42b6-bdbb-1dd2464ce89c

📥 Commits

Reviewing files that changed from the base of the PR and between 0507832 and 10a8b3b.

📒 Files selected for processing (3)
  • .github/workflows/claude-review.yml
  • CLAUDE.md
  • docs/docs/guides/getting-started/ai-development.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • CLAUDE.md
  • .github/workflows/claude-review.yml

Comment thread docs/docs/guides/getting-started/ai-development.md Outdated
The deep-review label prompt validated diffs in isolation (0 findings on
PR #340) while a prompted @claude session on the same diff found 5,
including a real residual bug. Close the gap in what the reviewer is told
to do, not the model:

- Evidence phase: chase PR claims into linked issues and run/job logs,
  then verify the fix empirically — reading the diff is not verification.
- Residual-risk hunt: enumerate how the addressed failure class could
  still occur; refute with evidence or post as a finding. A Residual risk
  section is now required in the summary review.
- Advisory tier: 🔵 Advisory for real limitations/trade-offs worth
  putting on the record. Summary-table only — never inline, so advisory
  notes cannot become fixer work items and spin the AI loop.
- PR-type-aware lens: keep the component checklist for bulma-ui/docs
  diffs; add an infra lens (guard bypasses, shell+jq edge cases,
  untrusted input, silent failures, idempotency) for .github/.claude/
  scripts diffs.
- Optional focus steer: a triage+ user may pre-post a PR comment starting
  with deep-review: — a new gate step verifies each candidate author's
  live role (newest steer per distinct author, newest-first, max 5 role
  checks, so an outsider can neither inject text nor displace a
  maintainer's steer), and injects it as a FOCUS block via a
  random-delimiter heredoc output. Fail-soft by design: API failures
  degrade to an unfocused review, never a red run.

Unchanged: opus model, 120 turns, dedupe marker, one-review invariant,
allowed_bots, show_full_output debug-only, the is_error gate.

Docs: mention the steer in the AI development guide's label table and
CLAUDE.md's deep-review sentence.

Fixes #341
@allxsmith
allxsmith force-pushed the ci/deep-review-investigate-341 branch from 10a8b3b to 0497843 Compare July 22, 2026 01:54
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://d4e97c5f.bestax.pages.dev

@allxsmith

Copy link
Copy Markdown
Owner Author

@claude run another deep review

@claude

claude Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Claude finished @allxsmith's task in 6m 49s —— View job


Deep review (round 2)

  • Gather context (current diff vs origin/main, prior review findings, PR history)
  • Evidence phase: verify PR claims against actual code/tests
  • Residual-risk hunt (infra lens: guard/permission bypasses, shell+jq edge cases, untrusted input, silent failures, idempotency/concurrency)
  • Check whether the prior "Screenshots at handoff" finding was addressed
  • Post findings

Evidence check. Current HEAD is 0497843 (CI green: CI, PR #347, Test deployment, Dependency Review all passed at 01:54 UTC). gh pr diff 347 against this HEAD shows only 3 hunks: the new Extract focus steer step + prompt changes in claude-review.yml, the one-sentence CLAUDE.md addition, and the label-table reformat in ai-development.md — the "Screenshots at handoff" paragraph flagged in the previous deep review round is gone. Confirmed by grep across the whole repo: no remaining reference to story-screenshots.yml or a handoff-screenshot feature anywhere in this PR's files. That paragraph described work from a separate, still-unmerged branch (feat/story-screenshots-handoff, commit 05efb18, not an ancestor of main or this branch) — dropping it was the right fix, not a workaround.

Re-traced the Extract focus steer step (claude-review.yml:154-222) end to end against its own invariants, independently of the previous pass's local test matrix:

  • reverse | reduce correctly yields newest-first, one-entry-per-distinct-author (comments API is oldest-first; reverse before the reduce is required and present; accumulator appends in newest→oldest visitation order, so index 0 stays the newest).
  • The || CANDIDATES='[]' fallback sits outside the $(...) substitution as claimed — under pipefail, a gh api failure makes the pipeline's exit status non-zero even though jq -s itself would exit 0, so the fallback fires correctly rather than corrupting the array shape.
  • case "$ROLE" in admin|maintain|write|triage) ... ; *) ... ;; esac correctly treats a none/null/read role (incl. the documented "public repo returns read for any user" quirk) as untrusted.
  • steps.focus and the Run Claude step share the identical if: guard, so ${{ steps.focus.outputs.block }} is never referenced when the step was skipped — no dangling-output edge case.

No new blocking defect found in this step. One pre-existing, already-disclosed advisory still applies and is unchanged by this round: the 5-distinct-author cap is applied before the permission check, so a burst of ≥5 other deep-review: comments after a real maintainer's steer can drop it from consideration (fails open to "unfocused review", not a security bypass — same conclusion as the prior round).

One additional minor/non-blocking observation (not worth a blocking tag): TEXT="${TEXT:0:2000}" (line 208) truncates by shell character count, which on GitHub Actions' default runner locale can split a multi-byte UTF-8 character at the 2000-char boundary, producing a garbled trailing byte sequence in the FOCUS block for a steer comment that's both non-ASCII and over the cap. Cosmetic only — worst case is a mangled last character in the prompt, not a failure — so not flagging it as a table row.

Residual risk: I did not find a way the original #340/#341 failure mode (deep-review missing evidence-backed residual bugs because it was never told to hunt for them) could still occur through this diff. The new steps 2–4 explicitly require fetching cited evidence and enumerating residual-risk variants before judging code — the same gap the first deep-review round on this PR (using the old, unstructured prompting style) actually caught: it found the real "Screenshots at handoff" documentation-of-nonexistent-feature bug and it's now fixed.

Overall: No blocking findings this round. The one prior blocking issue is resolved by removal; the one prior advisory (author-cap-before-permission-check ordering) remains a known, accepted trade-off, not a regression introduced since. This PR is in a mergeable state as far as this review is concerned — the only irreducible caveat (already called out in the PR body) is that the label-triggered workflow path itself can't be exercised against its own PR before merge, so the definitive test remains the planned post-merge re-run against PR #340.

@allxsmith
allxsmith merged commit 4aecef2 into main Jul 22, 2026
24 checks passed
@allxsmith
allxsmith deleted the ci/deep-review-investigate-341 branch July 22, 2026 10:50
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.6.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 5.8.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 1.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: make deep-review label reviews investigate like a prompted session — evidence phase, residual-risk hunt, focus steer, PR-type-aware lens

1 participant