test(reviewer): guard the comment-selection filter that has broken twice - #273
Conversation
`agy-review.sh`'s filter decides which PR comments the bot DELETES. It has been wrong twice, and neither time was observable from the outside — the review posted fine on both occasions: 1. The just-posted comment was not reliably excluded. `new_comment_id` came from re-querying the comment list, which races GitHub's read replication; on a miss the exclusion degenerated to `select(.id != null)` — true for every id — and the run deleted the review it had just published. 2. jq's `--arg`/`--argjson` were passed to `gh api`, which has no such flags. It exited non-zero, `2>/dev/null` hid the message, and `set -o pipefail` plus `set -e` killed the script AFTER posting. Stale comments silently accumulated (PR #272 reached four) and the job went red with nothing in the log to say why. Both bugs lived in the same ~10 lines, and the second was mine. - The filter moves into a named `SELECT_STALE_JQ` so a test can exercise the real thing rather than a copy that can drift. - `scripts/agy-review-selftest.sh` lifts `MARKER` and that filter out of the script by pattern and runs them against fixtures — offline, no `gh`, no network, no self-hosted runner. Lifting by pattern is deliberate: rename or reshape either declaration and the test fails loudly instead of quietly checking something stale. - Six checks: the just-posted comment is never selected; other users and other bots are ignored (the author filter is a security control — without it anyone could paste the marker into a comment and have the bot delete comments on the next run); bot comments without the marker are ignored; an id of 0 excludes nothing real; the exclusion is genuinely by value; and `--arg`/`--argjson` are not passed to `gh api`. - Wired into the `lint` job, so it runs on every PR rather than only where `agy` is installed. Verified by injecting each historical regression and confirming the suite fails at the matching check and exits 1: dropping the id exclusion fails "excludes whichever id it is given"; moving `--arg` back onto `gh api` fails the flag check. Healthy tree exits 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 28 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
WalkthroughThe review script now centralizes stale bot-comment selection in a readonly jq filter. An offline self-test validates filtering and ChangesReview comment selection
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 10✅ Passed checks (10 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 @.github/workflows/ci.yml:
- Line 105: Add a job-level permissions block to the lint job containing only
the read access required by checkout, specifically contents: read, while leaving
the existing agy-review-selftest.sh step unchanged.
In `@scripts/agy-review-selftest.sh`:
- Around line 96-101: Update the self-test around the gh api flag check to
detect --arg and --argjson anywhere in the same logical shell command, including
continuation lines after gh api. Alternatively, validate that these flags are
attached to the jq invocation rather than gh api, while preserving the existing
failure reporting and counter behavior.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: 624d2514-3a33-4260-be36-061b73d3c102
📒 Files selected for processing (3)
.github/workflows/ci.ymlscripts/agy-review-selftest.shscripts/agy-review.sh
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: lint
- GitHub Check: accuracysnes
- GitHub Check: test-light
🧰 Additional context used
📓 Path-based instructions (2)
.github/workflows/**
⚙️ CodeRabbit configuration file
.github/workflows/**: Flag third-party actions pinned to a branch rather than a tag or commit SHA, jobs without
a least-privilegepermissions:block, and secrets that could reach the log. The
ci-successjob is the required check and must depend on every gate it claims to
aggregate — a new job that is not in itsneeds:list is a gate nothing enforces.
Files:
.github/workflows/ci.yml
**/*
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*: Do not commit or vendor the generatedsnesdev_wiki/mirror; it is gitignored and intended only as a local reference.
Keep commits focused and use Conventional Commits:<type>(<scope>): <subject>, with an imperative subject of at most 72 characters.
Do not use emojis in code, comments, or commit messages.
Before opening a PR, ensure formatting, Clippy, workspace tests, the core embedded build, rustdoc with warnings denied, documentation coverage, and changelog requirements pass.
Ticket completion must be reflected in the relevantto-dos/sprint file.
**/*: Preserve the one-directional crate graph: chip crates must not depend on one another;rustysnes-coreties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keepdocs/STATUS.mdas the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNESv2.0orengine-lineageanchors as project releases.
Files:
scripts/agy-review.shscripts/agy-review-selftest.sh
🪛 Shellcheck (0.11.0)
scripts/agy-review.sh
[info] 112-116: Expressions don't expand in single quotes, use double quotes for that.
(SC2016)
🔇 Additional comments (2)
scripts/agy-review.sh (1)
103-118: LGTM!Also applies to: 603-603
scripts/agy-review-selftest.sh (1)
1-95: LGTM!Also applies to: 103-108
| # it has been wrong twice in ways nothing observed (it posted its review fine both times). | ||
| # This check is offline — fixtures and `jq`, no network, no `gh`, no self-hosted runner — | ||
| # so it runs here on every PR rather than only where `agy` is installed. | ||
| - run: bash scripts/agy-review-selftest.sh |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Security Misconfiguration (CWE-732): Incorrect Permission Assignment for Critical Resource
Declare least-privilege permissions for lint.
Line 105 runs PR-controlled code, but lint has no job-level permissions: block. Its token privileges therefore inherit repository defaults; declare only the read access checkout needs, such as contents: read.
As per path instructions, jobs must have a least-privilege permissions: block.
🤖 Prompt for 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.
In @.github/workflows/ci.yml at line 105, Add a job-level permissions block to
the lint job containing only the read access required by checkout, specifically
contents: read, while leaving the existing agy-review-selftest.sh step
unchanged.
Source: Path instructions
…silently Review fixes for #273. All three findings describe silent-failure modes in a test whose entire premise is that silent failure is what went wrong twice — so all three are taken. - The `sed` range that lifts `SELECT_STALE_JQ` ends at the first line closing with a quote, so a filter body that ever did so would be TRUNCATED — and a truncated jq program can still compile and still return ids, i.e. the test would keep passing while checking something that is not the shipped filter. Two independent guards now: the extracted program must compile, and it must end with the `| .id` projection that makes it a complete pipeline rather than a prefix of one. The compile check passes `--arg marker` / `--argjson new_id` itself: the filter references both, and jq rejects an undefined variable at compile time, so omitting them fails a perfectly good program. Caught by running it. - The `gh api ... --arg` scan was line-based, so moving the flag onto a continuation line would have produced a false pass on precisely the mistake the check exists to catch. Continuations are folded before matching. - `tr '\n' ' '` left a trailing space that every expected value had to mirror. `paste -sd' '` instead, and the expectations now read as what they are. Verified by injecting each new failure mode: dropping the final `| .id` trips the truncation guard; putting `--arg` on a continuation line trips the flag check. Both exit 1. Healthy tree passes all six checks and exits 0. shellcheck clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review response — all three taken, in ce99537No blocking issues. Every finding describes a silent failure mode in a test whose entire premise is that silent failure is what went wrong twice, so all three are taken rather than argued with. Suggestion 1 — fragile
|
| injection | result |
|---|---|
drop the final | .id from the filter |
FAIL: extracted SELECT_STALE_JQ does not end in '| .id'; extraction truncated — exit 1 |
move --arg onto a continuation line |
FAIL --arg/--argjson passed to \gh api`` — exit 1 |
| healthy tree | all six checks pass, exit 0 |
shellcheck -S warning clean.
Aside: this PR is the end-to-end proof of the #270 fix
Worth stating since it was the open question when this branch was opened. On this PR the review job passed (54s) — its first green since #270 broke it — and it left exactly one bot comment.
Both halves matter, and they fail independently: the job passing does not by itself prove the delete step runs, since the failure mode was "post fine, then die". One comment on a PR whose review has run is the positive evidence the gh api | jq pipeline now completes. For contrast, #272 accumulated four during the broken window.
Antigravity review (Gemini via Ultra)Adds an offline self-test script ( Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by |
What
scripts/agy-review.sh's comment-selection filter decides which PR comments the bot deletes. It has been wrong twice, and neither time was observable from the outside — the review posted fine on both occasions:new_comment_idcame from re-querying the comment list, which races GitHub's read replication; on a miss the exclusion degenerated toselect(.id != null)— true for every id — and the run deleted the review it had just published.--arg/--argjsonwere passed togh api, which has no such flags. It exited non-zero,2>/dev/nullhid the message, andset -o pipefail+set -ekilled the script after posting. Stale comments silently accumulated (this repo's chore(release): cut v1.25.0 "Workbench" #272 reached four) and the job went red with nothing in the log to say why.Both lived in the same ~10 lines. The second was mine, introduced in #270 and shipped for four PRs before the symptom was traced.
Changes
SELECT_STALE_JQ, so a test can exercise the real thing rather than a copy that drifts.scripts/agy-review-selftest.shliftsMARKERand that filter out of the script by pattern and runs them against fixtures — offline: nogh, no network, no self-hosted runner. Lifting by pattern is deliberate; rename or reshape either declaration and the test fails loudly instead of quietly checking something stale.lintjob, so it runs on every PR rather than only whereagyhappens to be installed.The six checks
--arg/--argjsonnot passed togh apiVerification
Each historical regression was injected and the suite confirmed to fail at the matching check and exit 1:
| select(.id != $new_id)→ fails "excludes whichever id it is given" (want [222 999],got [111 222 999]), 4 checks fail, exit 1--argback ontogh api→ fails the flag check, exit 1bash -nclean on both scripts;shellcheck -S warningclean on the new one;ci.ymlparses as YAML.Note
This PR is also the first one since the
gh api --argfix reachedmain. The reviewer workflow checks out the default branch by design (so a PR can never supply the code that reviews it), which is why the fix could not be validated on the release PR — the review job there ranmain's still-broken copy. Its behaviour here is the first real end-to-end proof.🤖 Generated with Claude Code
The change claims that stale review-comment cleanup now preserves the newly posted review and that the regression suite catches both historical failures: incorrect comment selection and invalid
gh apijq-flag placement. The claim would be false if the filter selected the new comment, mishandled IDs/authors/markers, accepted either historical regression, or if the self-test were not run successfully in CI.