Skip to content

release: v2.2.4 "Cartridge" — libretro core builds/installs for RetroArch#327

Merged
doublegate merged 9 commits into
mainfrom
chore/standardize-agy-reviewer
Jul 24, 2026
Merged

release: v2.2.4 "Cartridge" — libretro core builds/installs for RetroArch#327
doublegate merged 9 commits into
mainfrom
chore/standardize-agy-reviewer

Conversation

@doublegate

@doublegate doublegate commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Cuts v2.2.4 "Cartridge" — a libretro / RetroArch distribution release — and folds in the Antigravity reviewer standardization (2 commits).

Its purpose is that the RustyNES core builds and installs cleanly through the Libretro buildbot (git.libretro.com/libretro/RustyNES) so RetroArch users can pull it from the in-app Online Updater → Core Downloader.

Zero emulation-core changes

The deterministic #![no_std] chip stack, save-state / TAS / netplay formats, and every golden vector are byte-identical to v2.2.3AccuracyCoin holds 141/141 (100.00%), nestest 0-diff, by construction. This is a metadata + version + docs cut.

Libretro audit (the substantive work)

crates/rustynes-libretro wraps rustynes-core, so it inherits every v2.2.3 change automatically and needed no code change:

  • fast dot path default → the wrapper gets the ~11% win for free;
  • PPU_SNAPSHOT_VERSION 8 / APU v4 schema is transparent (get_serialize_size / on_serialize size and emit the current snapshot via Nes::snapshot_core_into, not a fixed layout) — RetroArch save states / run-ahead / rollback carry the v8 state with no wrapper change;
  • Mapper::mix_audio i32, the Zapper model, the mNNN_ rename are all below the public dependency surface.

Both buildbot cross-ABIs the CI gate models build clean:

cargo check --release -p rustynes-libretro --target x86_64-pc-windows-gnu    clean
cargo check --release -p rustynes-libretro --target aarch64-linux-android    clean

rustynes_libretro.info metadata corrected

  • disk_control falsetrue — the real fix. The FDS multi-side Disk Control interface has been wired since the buildbot recipe landed, but the .info advertised it as absent, hiding multi-disk FDS swapping from RetroArch's Quick Menu.
  • display_version v1.0.0v2.2.4; description mapper count 168172.
  • Core options remain a documented future enhancement (core_options = "false" is accurate, not stale).

Also in this PR

  • Antigravity reviewer standardization (1a2e791a) onto the shared template — the canonical version now across RustyNES / RustySNES / RustyN64.
  • Version bump 2.2.3 → 2.2.4 (all 18 crates); README / CHANGELOG / STATUS / AGENTS updated; ROADMAP.md re-synced from its stale v2.1.0 "current release" line to v2.2.4.

Verification

fmt · clippy --workspace --all-targets -D warnings (+ retroachievements combo) · no_std · markdownlint — all clean; AccuracyCoin 141/141 inherited unchanged.

Release mechanics

On merge, release-auto.yml tags v2.2.4, publishes with .github/release-notes/v2.2.4.md, and builds the platform binaries. The buildbot then mirrors the tag and RustyNES appears in RetroArch's Core Downloader.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Released RustyNES v2.2.4 “Cartridge” for libretro/RetroArch.
    • Enabled FDS disk control, including multi-disk swapping support.
    • Updated displayed version and mapper coverage information.
  • Bug Fixes

    • Corrected libretro core metadata to accurately advertise FDS capabilities.
  • Compatibility

    • Emulation behavior remains byte-identical to v2.2.3, with no expected breakage for saves, movies, or netplay.

Replaces RustyNES's bespoke `agy-review.sh` + workflow with the canonical
`Local_Only-Projects/antigravity-pr-review` template, so the three repos
that run this reviewer (RustyNES, RustySNES, RustyN64) stop diverging and a
future improvement to the template applies to all of them at once instead
of being re-invented per repo. After this change the two files are
byte-identical to the template.

WHY THIS WAS NEEDED. RustyNES's copy and the template had drifted into two
different solutions to the same problem. The template independently grew an
`AGY_DIFF_MODE=auto|inline|file` mechanism that hands a large diff to agy as
a single file under `.git/` (which git never lists in `status`, so it can't
show up as working-tree pollution) rather than truncating it. RustyNES,
without knowing the template had moved on, solved the same large-diff case
its own way -- splitting the diff into 150 KB parts under a gitignored work
dir. The template's approach is cleaner (one file, one read, no coverage
bookkeeping), so RustyNES adopts it.

THE ONE GENUINE GAP, NOW UPSTREAM. RustyNES's copy had exactly one thing the
template lacked and every installed repo needed: a fallback for GitHub's
20,000-line diff API limit. `gh pr diff` returns HTTP 406 for any diff over
that, and the template's `gh pr diff ... || exit 1` meant the review failed
outright on a large PR -- which is precisely the PR most worth reviewing (the
v2.2.3 mapper-rename PR was 67k lines). That fallback was ported into the
template and is part of this converged version: on a 406 (distinguished from
a genuine auth/network/bad-PR error, which stays fatal) it fetches the PR's
objects and the base branch into private `refs/agy/*`, takes `git merge-base`,
and computes the diff locally -- fetching objects but NEVER checking them out,
so the working tree stays on the default branch and a PR cannot rewrite its
own reviewer. The refs are removed on exit.

WHAT THE CONVERGED VERSION IS (union of the best of both):
  * the template's `AGY_DIFF_MODE` file/inline diff handling;
  * the 20,000-line API-limit local-git-diff fallback (from here);
  * RustyNES's security hardening, folded UPSTREAM so all three inherit it:
    the `isCrossRepository` fork gate in the script (a trusted commenter is
    not a trusted diff, and the diff is what agy ingests under
    --dangerously-skip-permissions), fail-closed metadata (a `gh pr view`
    failure must never be indistinguishable from "same-repo"), the
    `AGY_DRY_RUN` escape hatch for testing the acquisition + prompt-assembly
    path without spending an agy run, the default-branch checkout (the
    reviewer never runs PR-supplied code on the self-hosted runner),
    `persist-credentials: false`, and `actions/checkout@v7`;
  * the template's `synchronize` trigger (auto-re-review on every push),
    which is safe under the default-branch checkout since the PR supplies
    only the diff (data), never the reviewer code.

ONE RECONCILIATION TRAP FIXED IN THE STANDARD WORKFLOW. RustyNES's old
workflow set `MAX_DIFF_BYTES: "90000"` -- correct for the parts mechanism,
where that value was the inline budget. But the template script truncates
the diff to `MAX_DIFF_BYTES` BEFORE the inline/file decision, so carrying
that env onto the template script would have truncated every diff to 90 KB
and defeated file mode entirely. The standard workflow drops the override
(5 MB sanity-cap default) and documents why lowering it is wrong.

VERIFICATION. `bash -n` clean; `shellcheck -S warning` reports nothing beyond
the pre-existing SC1007/SC2218 in the template's config block; `actionlint`
clean against `.github/actionlint.yaml` (the `agy` self-hosted label is
declared there); the repo pre-commit hook passes on both files. The full
path -- fork gate -> 406 detection -> local `git diff` -> file-mode handoff --
was exercised end-to-end with `AGY_DRY_RUN=1` against this repo's own PR #325
(67,645 lines / 2.69 MB fetched via the fallback, handed off as a
`.git/agy-review-diff.*.patch` with a 2.6 KB prompt), and the `refs/agy/*`
and temp files were confirmed removed on exit.

No emulation-core, test, or build change -- reviewer tooling only.
…Arch

A libretro / RetroArch distribution cut. Its purpose is that the RustyNES
core builds and installs cleanly through the Libretro buildbot
(git.libretro.com/libretro/RustyNES) so RetroArch users can pull it from the
in-app Online Updater -> Core Downloader.

ZERO EMULATION-CORE CHANGES. The deterministic #![no_std] chip stack, the
save-state / TAS / netplay formats, and every golden vector are byte-identical
to v2.2.3, so AccuracyCoin holds 141/141 (100.00%) and nestest is 0-diff by
construction. This is a metadata + version + docs cut, not a code cut.

LIBRETRO AUDIT (the substantive work). crates/rustynes-libretro is a thin
wrapper over rustynes-core, so it inherits every recent change automatically
and needed no code change to carry them:

  * the fast PPU dot path is the core default now, so the wrapper gets the
    ~11% rendering-heavy win for free;
  * the PPU_SNAPSHOT_VERSION 8 / APU v4 save-state schema is transparent to
    the wrapper, because get_serialize_size / on_serialize size and emit the
    CURRENT snapshot via Nes::snapshot_core_into / VsDualSystem::snapshot, not
    a hardcoded layout -- so RetroArch save states, run-ahead, and rollback
    carry the v8 sprite-eval FSM + OAM data-bus state with no wrapper change
    (and since libretro save states are session-local, the ADR-0028 epoch
    never applies);
  * the Mapper::mix_audio i32 widening, the Zapper model, and the mNNN_ mapper
    rename are all below the crate's public dependency surface.

Both buildbot cross-ABIs the GitHub early-warning gate models build clean,
verified with the exact CI command:
  cargo check --release -p rustynes-libretro --target x86_64-pc-windows-gnu
  cargo check --release -p rustynes-libretro --target aarch64-linux-android

rustynes_libretro.info METADATA CORRECTED (the file RetroArch's core
downloader reads to learn the core's capabilities):

  * disk_control "false" -> "true" -- THE REAL FIX. The FDS multi-side Disk
    Control interface (enable_disk_control_interface() + the on_set_eject_state
    / on_get_image_index / on_get_num_images / on_replace_image_index callback
    trampolines) has been wired since the buildbot recipe landed, but the .info
    advertised it as absent, so RetroArch's Quick Menu -> Disk Control never
    surfaced multi-disk FDS swapping. A multi-side FDS game is now swappable
    from the RetroArch UI as intended.
  * display_version "v1.0.0" -> "v2.2.4" (stale since the v1.0.0 era; the
    runtime library_version the core reports has always tracked
    CARGO_PKG_VERSION -- this is the static metadata string RetroArch shows).
  * description mapper count 168 -> 172, plus a note that FDS multi-disk
    swapping runs through the Disk Control interface.

Libretro core options (region / overscan / palette / accuracy toggles) remain
unexposed: core_options = "false" is accurate, not stale -- the CoreOptions
impl is deliberately empty. Documented as a future enhancement rather than a
v2.2.4 gap, since it is a new capability deserving its own focused work +
determinism/save-state testing rather than a rushed add in a distribution cut.

TOOLING. The Antigravity PR reviewer standardization onto the shared template
(scripts/agy-review.sh + .github/workflows/antigravity-review.yml, the prior
commit on this branch) rides along in this release -- the same canonical
version now installed across RustyNES / RustySNES / RustyN64.

VERSION + DOCS. Workspace 2.2.3 -> 2.2.4 (all 18 crates inherit); README badge
+ Current Release; CHANGELOG [2.2.4]; docs/STATUS.md current-release block;
AGENTS.md MC-PROJECT block + the never-claim-later guard (-> v2.2.4);
to-dos/ROADMAP.md re-synced from its stale v2.1.0 "current release" line to
v2.2.4 with a compact release-line bridge (full per-release detail stays in
CHANGELOG.md + docs/STATUS.md, the single source of truth). .gitignore needs
no change -- the libretro build artifacts (.so/.dll/.dylib/.a) are already
covered.

VERIFICATION. cargo fmt --all --check; clippy --workspace --all-targets
-D warnings (+ the retroachievements feature combo); no_std build
(thumbv7em-none-eabihf); markdownlint (pinned v0.39.0) on every touched
document + the release notes; the codename parses to "Cartridge" for the
release-auto title. AccuracyCoin 141/141 is inherited unchanged from v2.2.3.

Mobile versions stay frozen at their v2.0.x host-only release points; the
joint store launch remains v2.3.0.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 27 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: ASSERTIVE

Plan: Pro Plus

Run ID: 0c9630e9-a484-4190-a8d2-101e26e43cff

📥 Commits

Reviewing files that changed from the base of the PR and between b2b613b and af51173.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • crates/rustynes-libretro/rustynes_libretro.info
  • to-dos/ROADMAP.md
📝 Walkthrough

Walkthrough

RustyNES v2.2.4 updates libretro metadata and release documentation without emulation-core changes. The Antigravity review workflow and script add synchronized PR triggers, bounded diff/prompt handling, fallback diff acquisition, safer execution, and improved failure diagnostics.

Changes

v2.2.4 release

Layer / File(s) Summary
Libretro metadata and version contract
Cargo.toml, crates/rustynes-libretro/rustynes_libretro.info
The workspace and libretro metadata are updated to v2.2.4, with disk control enabled and mapper coverage revised.
Release documentation and status
.github/release-notes/*, CHANGELOG.md, README.md, docs/STATUS.md, to-dos/ROADMAP.md, AGENTS.md
Release materials describe v2.2.4 as a libretro/RetroArch distribution cut and record unchanged emulation-core behavior and metadata corrections.

Antigravity review execution

Layer / File(s) Summary
Review script pipeline
scripts/agy-review.sh
Diff fetching, truncation, prompt budgeting, inline/file delivery, cleanup, and UTF-8 handling are refactored with bounded configuration.
Review execution and failure reporting
scripts/agy-review.sh
Locking, PTY execution, output normalization, retry handling, and stderr diagnostics are updated.
Workflow triggers and configuration
.github/workflows/antigravity-review.yml
PR synchronization triggers and review configuration comments/options are updated, including diff-size defaults and credential-safety documentation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 7 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Changelog Entry For User-Visible Changes ⚠️ Warning This PR has user-facing libretro metadata fixes, but CHANGELOG.md leaves [Unreleased] empty and puts the notes in a dated 2.2.4 section instead. Move the new notes into an [Unreleased] entry (or add an Unreleased bullet) and keep the dated 2.2.4 section only after the release is actually cut.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: the v2.2.4 Cartridge release for libretro/RetroArch builds and installation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Docs-As-Spec Sync ✅ Passed No files changed under crates/rustynes-cpu|ppu|apu|mappers in the PR diff, so there were no chip-behavior changes requiring docs/.md sync.
No Unwrap/Expect/Panic On Untrusted Input ✅ Passed PR only changes docs/config plus scripts/agy-review.sh; diff search found no new .unwrap(), .expect(), or panic! on changed source files.
Safety Comment On New Unsafe Blocks ✅ Passed The only changed file is scripts/agy-review.sh, and the patch adds no Rust unsafe {} blocks or unsafe fns.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/standardize-agy-reviewer

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

…d on this PR

The Antigravity reviewer ran against this PR and flagged five security
regressions in `scripts/agy-review.sh` -- each a case where standardizing onto
the shared template quietly WEAKENED what RustyNES's prior (main) reviewer
already did. The reviewer catching a regression in its own standardization is
the system working as intended; all five are fixed, and the fixes are also
committed upstream in the canonical template so re-installs carry them.

  1. Command injection in the `script(1)` PTY fallback (BLOCKING). The command
     handed to `script -qfec` (run via `sh -c`) interpolated `${flags[*]}` raw;
     the flag values come from env vars (AGY_MODEL / AGY_EFFORT /
     AGY_PRINT_TIMEOUT), so a shell metacharacter in any would be evaluated by
     the inner shell. Rebuilt with `printf '%q '` (main's proven form).
  2. Arbitrary comment deletion (BLOCKING). The prior-comment cleanup selected
     every comment CONTAINING the marker with no author filter, so any user
     could put the marker (an HTML comment) in a comment and have the bot delete
     arbitrary comments. Now scoped to
     `.user.type == "Bot" and .user.login == "github-actions[bot]"`.
  3. SQLite conversation-store data leak (BLOCKING). The empty-output fallback
     read the most-recent `*.db` under `$CONV_DIR` by mtime with no session
     scoping; on a shared runner that db can belong to an unrelated concurrent
     local `agy` session, leaking its output into a public PR comment. Removed
     entirely -- the PTY path + retry loop cover agy issue #76, and main never
     had it. Dead `$CONV_DIR` dropped with it.
  4. agy inherited the repo tokens (suggestion). agy runs under
     --dangerously-skip-permissions and ingests an untrusted diff yet inherited
     GH_TOKEN / GITHUB_TOKEN, which it never uses. Both invocations now run under
     `env -u GH_TOKEN -u GITHUB_TOKEN`.
  5. Missing issue_comment author re-check (suggestion). The workflow `if:`
     gates `/agy-review` on OWNER/MEMBER/COLLABORATOR, but the script -- also
     hand-runnable -- did not re-check it. The `author_association` gate is
     restored in the script's `issue_comment` branch as defense in depth.

This is the same reconciliation error that produced the standardized template:
the earlier pass ported RustyNES's LARGE-DIFF features into the template but not
its SECURITY surface, and wrongly concluded RustyNES had nothing to add. main's
version was harder in these five places; the template (and the just-merged
RustySNES / RustyN64 standardizations) inherited the weaker forms. RustySNES and
RustyN64 need a follow-up re-sync from the now-hardened template.

Verified `bash -n` clean and `shellcheck -S warning` clean beyond the
pre-existing SC1007/SC2218 config-block findings; the CHANGELOG Tooling entry
records the hardening.
@doublegate

Copy link
Copy Markdown
Owner Author

Adjudication of the Antigravity review — all five fixed (7ce7fc7f)

This is the reviewer catching a real regression in its own standardization, and it was right on every count. All three blocking issues and both suggestions are genuine: each is a place where standardizing RustyNES onto the shared template weakened what main's prior reviewer already did. The earlier reconciliation ported the template's large-diff handling but not main's security surface, and wrongly concluded RustyNES had nothing to add. Fixed here, and also upstream in the canonical template so re-installs carry the hardening.

Finding Fix
Blocking — script(1) command injection Rebuilt the fallback command with printf '%q ' so every token (including the env-derived flag values) is shell-escaped for the inner sh -cmain's proven form, replacing the raw ${flags[*]}.
Blocking — arbitrary comment deletion Deletion is now author-scoped: .user.type == "Bot" and .user.login == "github-actions[bot]", so a user planting the marker in a comment can no longer trigger deletion of arbitrary comments.
Blocking — SQLite data leak Removed the unscoped conversation-store fallback entirely. It read the newest *.db by mtime with no session scoping, so a concurrent local agy session's output could reach a public PR comment. The PTY path + retry loop cover agy issue #76 without it, and main never had it; the now-dead $CONV_DIR is dropped too.
Suggestion — token inheritance Both agy invocations now run under env -u GH_TOKEN -u GITHUB_TOKEN, so agy (under --dangerously-skip-permissions, ingesting an untrusted diff) does not inherit the repo tokens it never uses.
Suggestion — issue_comment author re-check Restored the author_association (OWNER/MEMBER/COLLABORATOR) gate in the script's issue_comment branch as defense in depth, so a hand-run or a lost workflow gate can't schedule agy for a stranger.

The nitpick (workflow env comments aligned with script defaults) is noted; the workflow's optional-override block already documents the same defaults.

Verified bash -n clean, shellcheck -S warning clean beyond the pre-existing SC1007/SC2218. Follow-up: RustySNES and RustyN64 merged the pre-fix standardized reviewer and need a re-sync from the hardened template.

…f_err, UTF-8 fallback

The re-review of the hardened reviewer (from the first fix commit) flagged three
more items; all addressed, and mirrored upstream in the canonical template:

  1. BLOCKING -- fixed /tmp log path. Standardizing dropped the run-id/PID suffix
     (a 6th regression from main), so a fixed /tmp/agy-review.log collides
     between concurrent jobs when RUNNER_TEMP is unset and is a predictable
     symlink/tamper target. Restored LOG=...agy-review-${GITHUB_RUN_ID:-$$}.log
     with the collision-avoidance comment (main's proven form).
  2. diff_err is now allocated alongside diff_file / meta_file up front, so the
     cleanup trap cannot reference it before it exists if the early metadata
     fetch exits (it was pre-declared empty, so this was hardening, not a bug).
  3. UTF-8 sanitize path gained a python3 fallback for hosts without iconv (a
     template feature main never had, so not a regression -- agy's point is a
     valid edge-case hardening): a no-iconv host could otherwise pass a
     truncation-split multibyte sequence to agy, and Rust std::env::args()
     panics on non-UTF-8. Both iconv and python3 absent is vanishingly rare on a
     CI runner.

bash -n + shellcheck clean beyond the pre-existing SC1007/SC2218.
@doublegate

Copy link
Copy Markdown
Owner Author

Adjudication of the second Antigravity pass — all addressed

The re-review of the hardened script caught three more, all fixed (and mirrored in the canonical template):

  • Blocking — fixed /tmp log path. A 6th regression from the standardization: it dropped the run-id/PID suffix. Restored LOG="${RUNNER_TEMP:-/tmp}/agy-review-${GITHUB_RUN_ID:-$$}.log" with the collision-avoidance comment — main's proven form — so concurrent jobs don't collide on a shared /tmp and the path isn't a predictable symlink target.
  • Suggestion — diff_err in the cleanup trap. diff_err is now allocated alongside diff_file / meta_file up front. (It was pre-declared empty, so rm -f "" was already harmless, but this removes the smell.)
  • Suggestion — iconv-missing UTF-8 edge. This is a template feature main never had, so not a regression — but the point is valid: added a python3 fallback (present on every CI runner) so a no-iconv host can't leave a truncation-split multibyte sequence in the prompt and panic agy's std::env::args(). Both iconv and python3 absent is vanishingly rare.

The nitpicks are noted. bash -n + shellcheck clean beyond the pre-existing SC1007/SC2218.

@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: 3

🤖 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 `@CHANGELOG.md`:
- Line 17: Keep v2.2.4 marked as Unreleased rather than presenting it as the
current release until its tag exists, or update its date to the actual release
day. Apply this consistently in CHANGELOG.md at lines 17-17, docs/STATUS.md at
lines 3-4, and to-dos/ROADMAP.md at line 49.

In `@crates/rustynes-libretro/rustynes_libretro.info`:
- Line 38: Update the description value in rustynes_libretro.info to use an
accurate mapper count, replacing “over 172 mapper families” with either “172
mapper families” or “over 168 mapper families.”

In `@to-dos/ROADMAP.md`:
- Line 50: Reconcile the stale release-state guidance by updating or archiving
the later ROADMAP statements that identify v1.10.0 as the latest tag and v2.0.0
as pending, while preserving the authoritative v2.2.4 release narrative at
to-dos/ROADMAP.md:50-50. Remove the contradictory version-policy guidance at
AGENTS.md:188-188 so both Markdown files consistently reflect the current
release state.
🪄 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: f3435a4f-5e0b-4038-826f-d3dcb3393fb2

📥 Commits

Reviewing files that changed from the base of the PR and between fb51805 and b2b613b.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !Cargo.lock
📒 Files selected for processing (10)
  • .github/release-notes/v2.2.4.md
  • .github/workflows/antigravity-review.yml
  • AGENTS.md
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • crates/rustynes-libretro/rustynes_libretro.info
  • docs/STATUS.md
  • scripts/agy-review.sh
  • to-dos/ROADMAP.md

Comment thread CHANGELOG.md
Comment thread crates/rustynes-libretro/rustynes_libretro.info Outdated
Comment thread to-dos/ROADMAP.md
doublegate and others added 2 commits July 23, 2026 20:00
… count

Addresses the CodeRabbit and agy review findings on PR #327.

`rustynes_libretro.info`: the RetroArch core-downloader description said
"over 172 mapper families", but 172 is the exact family count in the
honesty-gated mapper matrix (`docs/STATUS.md`), not a lower bound. "over
172" reads as ">172", overstating coverage. Corrected to the precise
"172 mapper families".

`to-dos/ROADMAP.md`: four current-state narratives predated the v2.0.0
"Timebase" tag and still asserted v1.10.0 "Arcade" as the latest tagged
release with v2.0.0 "code-complete on `main`, tag pending". v2.0.0
shipped 2026-07-03 and the release line has since advanced to v2.2.4
"Cartridge"; those statements now contradict the Status block at the top
of the file and the release chain in AGENTS.md/CHANGELOG. Reconciled:
- L40-41 blockquote: "code-complete on `main`, tag pending" -> "shipped
  as v2.0.0 on 2026-07-03", and the forward pointer relabelled to the
  "historical landing snapshot" section title.
- L59 bullet: "Preceding additive/platform release" / "This is the latest
  in an unbroken ... chain" -> "Historical anchor - the last v1.x
  release" / "It closed an unbroken ... chain" (past tense; v1.10.0 is no
  longer latest).
- L88 "Current state": v1.10.0-latest / v2.0.0-tag-pending -> "RustyNES
  v2.2.4 'Cartridge' is the latest tagged release ... v2.0.0 shipped
  2026-07-03", with the retained v2.0.0 paragraph explicitly flagged as a
  historical snapshot rather than a current-state claim.
- L90 section header: "code-complete, tag pending" -> "historical landing
  snapshot (shipped 2026-07-03; this section was written as it landed)".

No behavioural change; documentation-only reconciliation so agents and
readers receive one consistent version policy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
….git/

Adopts the agy first-pass suggestion on this PR (scripts/agy-review.sh:888 in the
diff): the on-disk large-diff handoff was written inside the hidden `.git/`
directory (`.git/agy-review-diff.$$.patch`), which risks a file-read failure if
the reviewer subagent or sandbox restricts tool access to `.git`. The `.git`-is-a-
worktree-gitfile fallback wrote `.agy-review-diff.$$.patch` at the repo ROOT,
where an untracked `.patch` pollutes `git status` if agy inspects working-tree
state -- defeating the reason `.git/` was chosen in the first place.

Write the diff into the dedicated, gitignored working-tree scratch dir instead,
`.agy-review-work/agy-review-diff.$$.patch` -- which this repo's `.gitignore`
has documented as the intended handoff location all along (lines 285-289), a
comment the script never actually honoured until now. This:
- removes the `.git/` tool-access dependency agy flagged;
- collapses the two-branch `.git`-vs-root fork into one gitignored path, so the
  transient .patch is never working-tree pollution regardless of whether `.git`
  is a real dir or a worktree gitfile;
- keeps per-run `$$` isolation, and the EXIT-trap cleanup now `rmdir`s the dir
  only when empty (concurrency-safe: a concurrent run's different-PID file is
  never clobbered; a SIGKILL leftover lands in a gitignored dir, not at root --
  addressing the reviewer's SIGKILL nitpick).

`.gitignore` already carries `/.agy-review-work/`, so no ignore change is needed
here. This mirrors the canonical template
(Local_Only-Projects/antigravity-pr-review) byte-for-byte in the diff-handoff
block so the standardized reviewer does not diverge across projects. No change to
diff content, prompt assembly, or the file-vs-inline decision.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@doublegate

Copy link
Copy Markdown
Owner Author

Adjudicating the agy first-pass suggestions (blocking issues: none) — all three are now addressed in 5e6b0721:

agy-review.sh:888 (Suggestion — .git/ handoff location): Adopted. The large-diff handoff no longer writes to .git/agy-review-diff.$$.patch (sandbox-read risk) or, on the worktree-gitfile fallback, to an untracked .patch at the repo root (which pollutes git status — the very thing the .git/ choice tried to avoid). It now writes to the dedicated, gitignored working-tree scratch dir .agy-review-work/agy-review-diff.$$.patch — which this repo's .gitignore (lines 285-289) had already documented as the intended handoff location; the script simply never honoured it until now. One code path, no .git-vs-root fork, no working-tree pollution.

agy-review.sh:888 (Nitpick — SIGKILL leftovers): Mitigated by the same change. A normal exit is already handled by the EXIT trap (it rm -fs the file and now rmdirs the dir only when empty, so a concurrent run's different-PID file is never clobbered). SIGKILL is inherently un-trappable, but a leftover now lands inside the gitignored .agy-review-work/ dir rather than at the repo root, so it is harmless and invisible to git status — the strongest guarantee available without a trappable signal.

agy-review.sh:821 (Suggestion — style_cap negative): Already handled, verified — no change needed. The clamp [ "$style_cap" -lt 0 ] && style_cap=0 runs on the same statement, immediately after style_cap=$(( MAX_PROMPT_BYTES - 8192 )) and before the head -c "$style_cap" on the next line, so head -c never sees a negative value. Even with MAX_PROMPT_BYTES set below 8192, style_cap clamps to 0 and head -c 0 yields an empty style guide (safe, well-defined) while the diff/file pointer still hands off in full.

The canonical template (Local_Only-Projects/antigravity-pr-review) carries the identical diff-handoff change plus the matching gitignore.snippet / install-into-repo.sh / README updates, so the standardized reviewer stays in sync across all three projects.

@doublegate

Copy link
Copy Markdown
Owner Author

Adjudicating the latest agy review's one blocking issue ("Unable to read diff file .agy-review-work/pr-327.part-000.diff") — this is a reviewer tooling limitation, not a defect in the reviewed change, and it does not block this PR. Root cause, from the run log (run 30055205903):

[agy-review] diff is 118338 bytes; handing it off on disk (over the 90000-byte inline budget)
[agy-review] wrote 1 diff part(s) to .agy-review-work/

The reviewer (the version on the default branch, which is what the workflow runs — it checks out main, never the PR head) wrote the handoff file correctly; agy's sandboxed file-reading tool then could not read it back. Two facts pin this as pre-existing and orthogonal to the change under review:

  1. First time this path was exercised on this PR. Every earlier head (7ce7fc7f, b2b613bf, 8299aa37) had a diff under the 90 KB inline budget, so it was reviewed inline and agy never touched the on-disk handoff. This head's diff (118 KB) is the first to cross the budget and hit the file path — so the failure surfaced now, but the code that fails is the deployed reviewer's, not this PR's.
  2. The 90–125 KB band is a genuine gap in the file-handoff design: too large to inline (the execve argv ceiling is ~128 KB, and boilerplate + style guide ride on top of the diff), yet agy's sandbox resolves its file tool against a different root than the shell CWD, so the relative-path handoff isn't found. This affects both the default branch's multi-part split handoff and this PR's single-file handoff — both write to .agy-review-work/ — so merging this PR neither introduces nor worsens the behavior; for a >90 KB diff both fail identically.

The review CI check (this same agy job) is green / completed/success — the job exits 0 and posts its review regardless of the verdict text — so nothing here blocks the merge or the v2.2.4 release automation (which does not depend on agy).

Follow-up (tracked, not release-blocking): the on-disk handoff needs a fix validated against the live agy runner — determine how agy's file tool resolves paths (absolute path vs a non-hidden gitignored dir vs feeding parts back inline within the argv budget) — and land it in the shared template so all three consuming repos get it at once. That is deliberately deferred to a dedicated change rather than blind-patched into this release PR, since it cannot be validated without the runner and the branch's reviewer does not execute until it is on the default branch.

The actual content of this PR (the v2.2.4 release docs + libretro .info fix + the reviewer's own security/robustness hardening) was reviewed on the prior inline passes — agy's b2b613bf review returned "Blocking issues: None found" with only the two adjudicated suggestions — and by CodeRabbit (all threads resolved).

doublegate and others added 2 commits July 23, 2026 20:34
…d-dir

The on-disk large-diff handoff (the `.agy-review-work/` file mode this PR
introduced) never actually worked: for a diff over the inline budget the
reviewer writes it to disk and tells agy to read it, but agy came back "the file
does not exist on the filesystem" and produced an empty review — which is exactly
what happened when this PR's own diff crossed the ~90 KB inline budget (below it
the diff is inlined and the file path is never exercised, which is why every
earlier head of #327 reviewed fine).

Root cause, proven on the live `agy` runner (this host is the self-hosted runner)
with a three-way probe under the exact review flags (`--sandbox
--dangerously-skip-permissions`):

  1) relative path `.agy-review-work/...`, no --add-dir  -> FAIL ("does not exist")
  2) same relative path + `--add-dir "$PWD"`             -> PASS
  3) absolute path, no --add-dir                         -> PASS

agy's sandboxed file tool does NOT resolve a relative path against the shell's
CWD — it resolves against agy's own workspace root, and under --sandbox the
checkout is not in that workspace — so a CWD-relative handoff path is never
found.

Fix, both halves (each independently sufficient per the probe; a confirming run
of the exact combined production-shaped invocation, an absolute path +
`--add-dir "$PWD" --sandbox --dangerously-skip-permissions`, also passed reading
a realistic ~400-line diff-shaped file with a token buried mid-file):

  - The prompt now hands agy the diff by ABSOLUTE path (`$agy_diff_file`) instead
    of the CWD-relative `$diff_name`.
  - `--add-dir "$PWD"` is appended to the flags, but ONLY in file-handoff mode
    (`use_file=1`). An inline review reads nothing from disk and keeps zero
    filesystem access, so the common path's prompt-injection surface is
    unchanged; only the large-diff path (which must read a file) gains workspace
    access to the checkout.

Mirrors the canonical template (Local_Only-Projects/antigravity-pr-review),
byte-identical in the handoff + flags regions, so the standardized reviewer stays
in sync across all three repos. NOTE: the reviewer that runs on a PR is always
the DEFAULT-BRANCH version (the workflow checks out `main`, never the PR head),
so this fix does not change the review of #327 itself — it takes effect for every
PR reviewed once #327 merges to main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ooling

Documents the reviewer handoff fix landed on this PR (absolute-path + --add-dir,
proven on the live runner) in the CHANGELOG's [2.2.4] Tooling section, keeping
the single source of truth for user-visible change in sync with the code
(docs-as-spec). No behavioural change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
doublegate added a commit that referenced this pull request Jul 24, 2026
The Antigravity reviewer, run against #328, flagged real robustness gaps in the
template it standardizes onto. Three fixed; the one "blocking" finding is a false
positive (adjudicated on the PR).

- **Negative/zero MAX_PROMPT_BYTES no longer bypasses the E2BIG guard.** The
  ceiling check was `[ "$MAX_PROMPT_BYTES" -le "$ARG_SIZE_CEILING" ]`, which a
  negative value satisfies — so the clamp was skipped and the later
  `head -c "$MAX_PROMPT_BYTES"` prompt cap ran as GNU `head -c -1` (print
  all-but-last-byte), leaving the prompt effectively uncapped and re-exposing the
  execve E2BIG failure the cap exists to prevent. Now requires a POSITIVE integer
  at or below the ceiling; negative, zero, and non-numeric all clamp to the
  ceiling.

- **UTF-8 sanitize no longer risks wiping the prompt.** The iconv / python3
  branches did `mv "$prompt_file.utf8" "$prompt_file"` on tool exit 0 without
  checking the result is non-empty; a sanitizer that exits 0 but emits zero bytes
  would blank the prompt and hand agy nothing. Both branches now gate the mv on
  `[ -s "$prompt_file.utf8" ]` and otherwise discard the temp and keep the
  original.

- **Latent clamp-time crash fixed (found while hardening the clamp).** log() was
  defined AFTER the configuration block, but the MAX_PROMPT_BYTES clamp calls
  log() — so a clamp that fired died with `log: command not found` under set -e
  instead of warning and continuing. Moved log() / have_text() above the config
  block. This never bit in practice only because the default 125000 is valid and
  never triggers the clamp; the negative-value fix above makes the clamp
  reachable, so the ordering had to be correct.

Not changed (adjudicated on #328): the "blocking" claim that $truncated is never
surfaced — it is, at the comment-body assembly (`printf '%s' "$truncated"`); the
unbuffer-vs-script prompt passing "inconsistency" — both ultimately pass the same
`--print "$(cat "$prompt_file")"` argv (the script path via _agy_print.sh), so it
is behaviourally identical; and the pre-truncated-handoff nitpick — MAX_DIFF_BYTES
is the 5 MB pathological-diff sanity cap, documented as not-for-bounding-reviews.

Mirrors the canonical template byte-identically. #327's copy is synced to main's
reviewer when it rebases post-merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR bumps the workspace version to v2.2.4 ("Cartridge") and updates repository documentation, release notes, and libretro core metadata (disk_control = "true").

Blocking issues

None found.

Suggestions

  • CHANGELOG.md:L58-L74 & .github/release-notes/v2.2.4.md:L38-L40: The changelog and release notes document changes to scripts/agy-review.sh and .github/workflows/antigravity-review.yml (reviewer security hardening and large-diff handoff fixes), but neither file is present in the PR diff. Include the script and workflow changes in this PR or update the release notes to match what is actually committed.

Nitpicks

  • AGENTS.md:L188: The version narrative block in AGENTS.md continues to append historical details into single multi-thousand-character sentences; refactor into structured bullet points in a future cleanup pass.

Automated first-pass review by agy on a self-hosted runner -- not a human review.

@doublegate
doublegate merged commit a1df7b9 into main Jul 24, 2026
27 checks passed
@doublegate
doublegate deleted the chore/standardize-agy-reviewer branch July 24, 2026 01:52
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