Skip to content

chore(release): cut v1.25.0 "Workbench" - #272

Merged
doublegate merged 4 commits into
mainfrom
release/v1.25.0
Jul 30, 2026
Merged

chore(release): cut v1.25.0 "Workbench"#272
doublegate merged 4 commits into
mainfrom
release/v1.25.0

Conversation

@doublegate

@doublegate doublegate commented Jul 30, 2026

Copy link
Copy Markdown
Owner

What

Version ceremony for the frontend-parity release. The ten increments T-FP-AT-FP-G2 (PRs #260, #271, #262#269) are already on main and green; this promotes them from [Unreleased] to a named, tagged release and repairs the documentation drift that had accumulated alongside them.

No code changes — manifests, docs, and .gitignore only.

Why MINOR

Every item in the release is additive and default-off or default-inert: video.stack defaults to Off, the preset chain is empty, the debugger needs debug-hooks, GPU timing needs gpu-timing, and a unity per-voice gain is a bit-exact bypass in the DSP. An existing config.toml renders and sounds byte-identically.

Save-state FORMAT_VERSION stays at 10 — no format break, which is what would have forced a MAJOR under docs/adr/0006.

Changes

File Change
Cargo.toml + 16 member manifests 1.24.01.25.0 (rustysnes-libretro inherits via version.workspace); lock regenerated
CHANGELOG.md [Unreleased]## [1.25.0] "Workbench" - 2026-07-30 with a release summary; fresh empty [Unreleased] left for the next cycle
docs/STATUS.md Current release moved; new paragraph stating this changes no emulation behaviour and that the accuracy dashboard / pass counts / coprocessor matrix below are unchanged
README.md Version badge; Current Release rewritten; Roadmap gained three undocumented rungs
to-dos/VERSION-PLAN.md Full v1.25.0 "Workbench" rung in house style, per-ticket
.gitignore Ignores for the new artifact types (perf CSVs, .y4m/.wav captures, dropped-in presets and symbol maps)

Two things worth a reviewer's attention

README.md's Current Release section was stale at v1.8.0 — seventeen releases behind. That is a drift repair rather than a bump, and the Roadmap list had similarly stopped at v1.8.0; three rungs' worth of history (v1.9.0v1.20.0, v1.21.0v1.24.0, v1.25.0) were added.

The .gitignore entries are anchored and specifically named (/*.wav, /rustysnes-*.png, /*.y4m) rather than bare *.wav/*.png globs. A bare glob would also swallow committed test fixtures and the screenshots/ assets the README embeds. Verified screenshots/montage.png is still tracked (git check-ignore reports it unignored).

Verification

  • cargo test --workspace804 passed, 0 failed, 1 ignored
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • Per-feature frontend clippy across all eight sets (default, full, emu-thread, debug-hooks, cheats, scripting, hd-pack, gpu-timing). --features full excludes emu-thread, so default+full alone is not sufficient — that exact gap has shipped a real bug before
  • no_std core builds for thumbv7em-none-eabihf
  • cargo fmt --check and RUSTDOCFLAGS=-D warnings cargo doc --workspace — clean
  • markdownlint clean on every edited region. Residual MD060 findings are a newer-linter rule the project's own docs note is ungated in CI; the two MD024 hits are pre-existing at CHANGELOG lines 1698/3489

After merge

Tag v1.25.0 and publish the release with full notes.

🤖 Generated with Claude Code

Summary

Cuts the v1.25.0 “Workbench” release, promoting the existing frontend-parity work from Unreleased without changing emulator-core behavior.

  • Updates workspace and crate versions, lockfile, release documentation, roadmap, and generated-artifact ignore rules.
  • AccuracySNES dossier assertions remain unchanged: accuracy dashboard, per-suite pass counts, and coprocessor tier matrix are preserved; the coverage denominator does not move.
  • No observable emulation behavior changes are claimed. Save-state FORMAT_VERSION remains 10; new functionality is default-off or inert.
  • Verification reports 804 passed, 0 failed, and 1 ignored across workspace, feature-set, no_std, formatting, documentation, and markdown checks.

The release claim would be false if any frontend feature changes default emulator behavior, the save-state format or accuracy coverage changes, the dossier denominator or pass assertions differ, or the stated verification matrix fails.

Version ceremony for the frontend-parity release: the ten increments T-FP-A
through T-FP-G2 (PRs #260, #271, #262-#269) are on `main` and green, so this
promotes them from `[Unreleased]` to a named, tagged release.

## Version bump

`1.24.0` -> `1.25.0` across the workspace manifest and all 16 member crates
(`rustysnes-libretro` inherits via `version.workspace = true`). `Cargo.lock`
regenerated; `cargo metadata` and a full `cargo check --workspace` confirm the
graph still resolves.

MINOR, not MAJOR or PATCH, and the classification is load-bearing rather than
habitual: every item is additive and default-off or default-inert
(`video.stack` defaults to `Off`, the preset chain is empty, the debugger needs
`debug-hooks`, GPU timing needs `gpu-timing`, a unity per-voice gain is a
bit-exact bypass in the DSP), so an existing `config.toml` renders and sounds
byte-identically. Save-state `FORMAT_VERSION` stays at 10 — no format break,
which is what would have forced a MAJOR under `docs/adr/0006`.

## Documents updated

- `CHANGELOG.md` — `[Unreleased]` promoted to `## [1.25.0] "Workbench" -
  2026-07-30` with a release summary: the five headline capabilities, the
  additive/default-off posture, and a named section on the recurring dead-config
  defect class this release exists to remove. A fresh empty `[Unreleased]` is
  left in place for the next cycle.
- `docs/STATUS.md` — the single source of truth. `Current release` moved to
  `v1.25.0 "Workbench"` with `v1.24.0` demoted into the precedent list, plus a
  paragraph stating explicitly that this release changes no emulation behaviour,
  that the accuracy dashboard / per-suite pass counts / coprocessor tier matrix
  below it are unchanged, and naming the two engine-side fixes that rode along
  (`Cpu::interrupts_taken`; the `delta::Chain` front-eviction defect, which
  affected the rewind buffer as much as the new greenzone).
- `README.md` — version badge to v1.25.0, and the `Current Release` section
  rewritten. That section was **stale at v1.8.0** — seventeen releases behind —
  so this is a drift repair, not just a bump. The Roadmap list gained the three
  rungs that had accumulated undocumented (`v1.9.0`-`v1.20.0`,
  `v1.21.0`-`v1.24.0`, `v1.25.0`).
- `to-dos/VERSION-PLAN.md` — a full `v1.25.0 "Workbench"` rung in the house
  style: per-ticket detail for A/B/C1/C2/D/E/F/G1/G2, the dead-config rule that
  came out of it ("grep a setting's readers, not its definition"), the two
  engine-side fixes, and the verification matrix.
- `.gitignore` — ignores for the artifact types this release can now produce:
  perf CSV session logs, `.y4m`/`.wav` captures, and dropped-in shader presets
  and symbol maps. Deliberately **anchored and specifically named** (`/*.wav`,
  `/rustysnes-*.png`) rather than bare `*.wav`/`*.png` globs, which would also
  swallow committed test fixtures and `screenshots/`. Verified
  `screenshots/montage.png` is still tracked.

`to-dos/ROADMAP.md` already carried the `T-FP-*` rows as `landed v1.25.0`; no
change needed.

## Verification

- `cargo test --workspace`: 804 passed, 0 failed, 1 ignored.
- `cargo clippy --workspace --all-targets -- -D warnings`: clean.
- Per-feature frontend clippy across all eight sets (default, full, emu-thread,
  debug-hooks, cheats, scripting, hd-pack, gpu-timing). The `full` set excludes
  `emu-thread`, so default+full alone is not sufficient — this gap has shipped a
  real bug before.
- `no_std` core builds for `thumbv7em-none-eabihf`.
- `cargo fmt --check` and `RUSTDOCFLAGS=-D warnings cargo doc --workspace` clean.
- `markdownlint` clean on every edited region. The residual MD060 findings are a
  newer-linter rule the project's own docs note is ungated in CI, and the two
  MD024 hits are pre-existing at lines 1698/3489 of the CHANGELOG's history.

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

coderabbitai Bot commented Jul 30, 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: 22 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: 4be7d51c-5cd8-4d0a-9514-cd88b95fbb45

📥 Commits

Reviewing files that changed from the base of the PR and between b99d7e4 and d5c8886.

📒 Files selected for processing (2)
  • .gitignore
  • scripts/agy-review.sh

Walkthrough

The release preparation updates the project and crate versions to 1.25.0, adds a savestate dependency, documents the Workbench release and roadmap, and ignores frontend capture and shader-related artifacts.

Changes

Workbench 1.25.0 release preparation

Layer / File(s) Summary
Package version alignment
Cargo.toml, crates/*/Cargo.toml
Workspace and crate versions change from 1.24.0 to 1.25.0; rustysnes-savestate adds thiserror without default features.
Release documentation
CHANGELOG.md, README.md, docs/STATUS.md, to-dos/VERSION-PLAN.md
Release metadata, feature descriptions, roadmap entries, status details, defect notes, and verification expectations are updated for Workbench 1.25.0.
Frontend artifact exclusions
.gitignore
Adds ignore rules for capture, telemetry, shader preset, symbol, map, media, CSV, and PNG artifacts.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 10
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses a valid Conventional Commits form and accurately describes the release cut to v1.25.0 "Workbench".
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Changelog Entry ✅ Passed CHANGELOG.md is included in the full PR diff against origin/main, with a 44-line release entry for 1.25.0.
Docs-As-Spec ✅ Passed PASS: crates/rustysnes-* only changed package versions and one manifest dependency; no source behavior changed, so no docs/.md spec update was required.
Accuracysnes Bookkeeping ✅ Passed No files under tests/roms/AccuracySNES/gen/src/ changed in the PR diff against origin/main, so the AccuracySNES bookkeeping check is not triggered.
No Panic On Untrusted Input ✅ Passed Only manifests/docs/.gitignore/Cargo.lock changed; no source-code hunks added any .unwrap(), .expect(), or panic!() on external-input paths.
Safety Comment On New Unsafe ✅ Passed The release commit changes only docs/manifests/.gitignore/Cargo.lock; no Rust source files or added unsafe blocks/fns appear in the diff.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks

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

@github-actions

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR cuts release v1.25.0 "Workbench" by bumping crate versions across Cargo.toml and Cargo.lock, adding frontend output rules to .gitignore, and updating release documentation in CHANGELOG.md, README.md, docs/STATUS.md, and to-dos/VERSION-PLAN.md.

Blocking issues

None found.

Suggestions

  • CHANGELOG.md (lines 12–50) / docs/STATUS.md (lines 44–61): The release notes detail several feature increments (T-FP-A through T-FP-G2) and critical core fixes (Cpu::interrupts_taken bookkeeping and delta::Chain eviction logic), but no crate code or test updates are present in this diff; ensure all constituent PRs and test validations are already merged into main before tagging v1.25.0.
  • .gitignore (lines 246–250): Root-anchored patterns (/*.y4m, /*.wav, /rustysnes-*.png) will fail to ignore capture files generated if the binary is executed from within subdirectories or test runners outside the repository root. Consider scoping outputs strictly to /captures/ or using recursive patterns like **/*.y4m.

Nitpicks

  • README.md (line 17): Ensure the git tag v1.25.0 is published concurrently with merging to prevent broken badge links on the repository landing page.

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

@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 `@to-dos/VERSION-PLAN.md`:
- Around line 1504-1509: Update the Workbench increment count from ten to nine
to match the nine documented labels. Apply this correction in
to-dos/VERSION-PLAN.md lines 1504-1509, CHANGELOG.md lines 14-17, docs/STATUS.md
lines 44-46, and README.md lines 670-673; make no other documentation changes.
🪄 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: 1c8ff9e9-b8ec-499c-9b1a-5f6af2346391

📥 Commits

Reviewing files that changed from the base of the PR and between e8d8134 and b99d7e4.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock and included by Cargo.lock
📒 Files selected for processing (22)
  • .gitignore
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • crates/rustysnes-android/Cargo.toml
  • crates/rustysnes-apu/Cargo.toml
  • crates/rustysnes-cart/Cargo.toml
  • crates/rustysnes-cheevos/Cargo.toml
  • crates/rustysnes-core/Cargo.toml
  • crates/rustysnes-cpu/Cargo.toml
  • crates/rustysnes-frontend/Cargo.toml
  • crates/rustysnes-gfx-shaders/Cargo.toml
  • crates/rustysnes-ios/Cargo.toml
  • crates/rustysnes-mobile/Cargo.toml
  • crates/rustysnes-monetization/Cargo.toml
  • crates/rustysnes-netplay/Cargo.toml
  • crates/rustysnes-ppu/Cargo.toml
  • crates/rustysnes-savestate/Cargo.toml
  • crates/rustysnes-script/Cargo.toml
  • crates/rustysnes-test-harness/Cargo.toml
  • docs/STATUS.md
  • to-dos/VERSION-PLAN.md
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: lint
  • GitHub Check: accuracysnes
  • GitHub Check: test-light
  • GitHub Check: build
  • GitHub Check: build demo + docs
⚠️ CI failures not shown inline (2)

GitHub Actions: Antigravity PR Review / review: chore(release): cut v1.25.0 "Workbench"

Conclusion: failure

View job details

##[group]Run chmod +x scripts/agy-review.sh scripts/_agy_print.sh
 �[36;1mchmod +x scripts/agy-review.sh scripts/_agy_print.sh�[0m
 �[36;1mscripts/agy-review.sh�[0m
 shell: /usr/bin/bash -e {0}
 env:
   GH_***REDACTED_SECRET_ASSIGNMENT***
   AGY_EFFORT: high
 ##[endgroup]
 [agy-review] reviewing doublegate/RustySNES#272
 [agy-review] diff is 27279 bytes; inlined into the prompt
 [agy-review] running agy via unbuffer (allocates a PTY) [attempt 1/3]
 ##[error]Process completed with exit code 1.

GitHub Actions: Antigravity PR Review / 0_review.txt: chore(release): cut v1.25.0 "Workbench"

Conclusion: failure

View job details

##[group]Run chmod +x scripts/agy-review.sh scripts/_agy_print.sh
 �[36;1mchmod +x scripts/agy-review.sh scripts/_agy_print.sh�[0m
 �[36;1mscripts/agy-review.sh�[0m
 shell: /usr/bin/bash -e {0}
 env:
   GH_***REDACTED_SECRET_ASSIGNMENT***
   AGY_EFFORT: high
 ##[endgroup]
 [agy-review] reviewing doublegate/RustySNES#272
 [agy-review] diff is 27279 bytes; inlined into the prompt
 [agy-review] running agy via unbuffer (allocates a PTY) [attempt 1/3]
 ##[error]Process completed with exit code 1.
🧰 Additional context used
📓 Path-based instructions (12)
**/Cargo.toml

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Do not configure the scripting and script-wasm backends as simultaneously enabled features; they are mutually exclusive.

Files:

  • crates/rustysnes-gfx-shaders/Cargo.toml
  • crates/rustysnes-monetization/Cargo.toml
  • crates/rustysnes-script/Cargo.toml
  • crates/rustysnes-cheevos/Cargo.toml
  • crates/rustysnes-apu/Cargo.toml
  • crates/rustysnes-ios/Cargo.toml
  • Cargo.toml
  • crates/rustysnes-cart/Cargo.toml
  • crates/rustysnes-netplay/Cargo.toml
  • crates/rustysnes-test-harness/Cargo.toml
  • crates/rustysnes-core/Cargo.toml
  • crates/rustysnes-mobile/Cargo.toml
  • crates/rustysnes-android/Cargo.toml
  • crates/rustysnes-cpu/Cargo.toml
  • crates/rustysnes-frontend/Cargo.toml
  • crates/rustysnes-ppu/Cargo.toml
  • crates/rustysnes-savestate/Cargo.toml
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Do not commit or vendor the generated snesdev_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 relevant to-dos/ sprint file.

**/*: Preserve the one-directional crate graph: chip crates must not depend on one another; rustysnes-core ties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keep docs/STATUS.md as the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNES v2.0 or engine-lineage anchors as project releases.

Files:

  • crates/rustysnes-gfx-shaders/Cargo.toml
  • crates/rustysnes-monetization/Cargo.toml
  • crates/rustysnes-script/Cargo.toml
  • crates/rustysnes-cheevos/Cargo.toml
  • crates/rustysnes-apu/Cargo.toml
  • crates/rustysnes-ios/Cargo.toml
  • Cargo.toml
  • crates/rustysnes-cart/Cargo.toml
  • crates/rustysnes-netplay/Cargo.toml
  • crates/rustysnes-test-harness/Cargo.toml
  • crates/rustysnes-core/Cargo.toml
  • crates/rustysnes-mobile/Cargo.toml
  • crates/rustysnes-android/Cargo.toml
  • crates/rustysnes-cpu/Cargo.toml
  • crates/rustysnes-frontend/Cargo.toml
  • crates/rustysnes-ppu/Cargo.toml
  • crates/rustysnes-savestate/Cargo.toml
  • README.md
  • docs/STATUS.md
  • CHANGELOG.md
  • to-dos/VERSION-PLAN.md
crates/rustysnes-*/**/*

📄 CodeRabbit inference engine (Custom checks)

For the full pull request diff against its base branch, any observable behavior change under crates/rustysnes-<chip>/ must be accompanied by an edit to the matching docs/<chip>.md; a crate change passes without documentation only when it does not alter observable behavior, with the non-behavioral change stated explicitly.

Files:

  • crates/rustysnes-gfx-shaders/Cargo.toml
  • crates/rustysnes-monetization/Cargo.toml
  • crates/rustysnes-script/Cargo.toml
  • crates/rustysnes-cheevos/Cargo.toml
  • crates/rustysnes-apu/Cargo.toml
  • crates/rustysnes-ios/Cargo.toml
  • crates/rustysnes-cart/Cargo.toml
  • crates/rustysnes-netplay/Cargo.toml
  • crates/rustysnes-test-harness/Cargo.toml
  • crates/rustysnes-core/Cargo.toml
  • crates/rustysnes-mobile/Cargo.toml
  • crates/rustysnes-android/Cargo.toml
  • crates/rustysnes-cpu/Cargo.toml
  • crates/rustysnes-frontend/Cargo.toml
  • crates/rustysnes-ppu/Cargo.toml
  • crates/rustysnes-savestate/Cargo.toml
**/*.{rs,toml}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,toml}: Additive features must be default-off so shipped/native, no_std, and wasm builds remain byte-identical.
Never use or configure --all-features; validate opt-in feature combinations individually as required by the project recipe.

Files:

  • crates/rustysnes-gfx-shaders/Cargo.toml
  • crates/rustysnes-monetization/Cargo.toml
  • crates/rustysnes-script/Cargo.toml
  • crates/rustysnes-cheevos/Cargo.toml
  • crates/rustysnes-apu/Cargo.toml
  • crates/rustysnes-ios/Cargo.toml
  • Cargo.toml
  • crates/rustysnes-cart/Cargo.toml
  • crates/rustysnes-netplay/Cargo.toml
  • crates/rustysnes-test-harness/Cargo.toml
  • crates/rustysnes-core/Cargo.toml
  • crates/rustysnes-mobile/Cargo.toml
  • crates/rustysnes-android/Cargo.toml
  • crates/rustysnes-cpu/Cargo.toml
  • crates/rustysnes-frontend/Cargo.toml
  • crates/rustysnes-ppu/Cargo.toml
  • crates/rustysnes-savestate/Cargo.toml
crates/**

⚙️ CodeRabbit configuration file

crates/**: Emulator core. Hot paths are allocation-free; unsafe requires a // SAFETY: comment
naming the invariant. Any change to save-stated fields needs a FORMAT_VERSION bump and a
docs/adr/0006 bump-log entry. Behavior changes must update the matching docs/<chip>.md
in the same change.

Files:

  • crates/rustysnes-gfx-shaders/Cargo.toml
  • crates/rustysnes-monetization/Cargo.toml
  • crates/rustysnes-script/Cargo.toml
  • crates/rustysnes-cheevos/Cargo.toml
  • crates/rustysnes-apu/Cargo.toml
  • crates/rustysnes-ios/Cargo.toml
  • crates/rustysnes-cart/Cargo.toml
  • crates/rustysnes-netplay/Cargo.toml
  • crates/rustysnes-test-harness/Cargo.toml
  • crates/rustysnes-core/Cargo.toml
  • crates/rustysnes-mobile/Cargo.toml
  • crates/rustysnes-android/Cargo.toml
  • crates/rustysnes-cpu/Cargo.toml
  • crates/rustysnes-frontend/Cargo.toml
  • crates/rustysnes-ppu/Cargo.toml
  • crates/rustysnes-savestate/Cargo.toml
crates/*/Cargo.toml

⚙️ CodeRabbit configuration file

crates/*/Cargo.toml: The frontend pins one compatible GUI tier (currently winit 0.30 / wgpu 29 / egui 0.35 and
its egui-wgpu/egui-winit siblings). Flag a bump to any one of those that is not
accompanied by matching bumps to the others. Optional features are default-off by
convention so shipped, no_std and wasm builds stay byte-identical; flag a new feature added
to a default = [...] list.

Files:

  • crates/rustysnes-gfx-shaders/Cargo.toml
  • crates/rustysnes-monetization/Cargo.toml
  • crates/rustysnes-script/Cargo.toml
  • crates/rustysnes-cheevos/Cargo.toml
  • crates/rustysnes-apu/Cargo.toml
  • crates/rustysnes-ios/Cargo.toml
  • crates/rustysnes-cart/Cargo.toml
  • crates/rustysnes-netplay/Cargo.toml
  • crates/rustysnes-test-harness/Cargo.toml
  • crates/rustysnes-core/Cargo.toml
  • crates/rustysnes-mobile/Cargo.toml
  • crates/rustysnes-android/Cargo.toml
  • crates/rustysnes-cpu/Cargo.toml
  • crates/rustysnes-frontend/Cargo.toml
  • crates/rustysnes-ppu/Cargo.toml
  • crates/rustysnes-savestate/Cargo.toml
Cargo.toml

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Cargo.toml: Keep crate dependencies one-directional; chip crates must not depend on each other.
Additive features must remain default-off so native, no_std, and wasm outputs remain byte-identical in default configurations.
Do not use cargo clippy --all-features; use cargo clippy --workspace --all-targets -- -D warnings, with explicit feature jobs when needed.

Files:

  • Cargo.toml
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Chip-behavior changes must update both the chip implementation and the corresponding docs/<subsystem>.md documentation.

A chip change must update both the chip implementation and its corresponding docs/<chip>.md documentation in the same change.

Files:

  • README.md
  • docs/STATUS.md
  • CHANGELOG.md
  • to-dos/VERSION-PLAN.md
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Docs are the spec, not a changelog. Flag prose that has drifted from the code it describes
rather than style nits. The markdownlint gate is pinned to v0.39.0 via pre-commit —
do not report rules that version does not have (MD060 in particular).

Files:

  • README.md
  • docs/STATUS.md
  • CHANGELOG.md
  • to-dos/VERSION-PLAN.md
docs/**/*.md

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Before changing a subsystem, consult docs/architecture.md, docs/STATUS.md, CONTRIBUTING.md, the relevant subsystem documentation, and applicable ADRs.

New subsystems must add documentation under docs/.

Files:

  • docs/STATUS.md
docs/**

⚙️ CodeRabbit configuration file

docs/**: Docs are the spec, not a history log. Flag claims that contradict the code, counts that
contradict the generated docs/accuracysnes-coverage.md, and any statement of coverage that
is broader than what the corresponding test actually asserts.

Files:

  • docs/STATUS.md
CHANGELOG.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

User-visible changes must be recorded under the [Unreleased] section.

For the full pull request diff against its base branch, modify CHANGELOG.md when user-visible behavior changes, including emulator output, frontend features, CLI flags, public APIs, or AccuracySNES cartridge contents. Do not require it for purely internal changes, tests, comments, or CI configuration.

Files:

  • CHANGELOG.md
🔇 Additional comments (24)
.gitignore (1)

240-256: LGTM!

Cargo.toml (1)

25-25: LGTM!

crates/rustysnes-android/Cargo.toml (1)

4-4: LGTM!

crates/rustysnes-apu/Cargo.toml (1)

3-3: LGTM!

crates/rustysnes-cart/Cargo.toml (1)

3-3: LGTM!

crates/rustysnes-savestate/Cargo.toml (1)

3-3: LGTM!

Also applies to: 10-10

crates/rustysnes-script/Cargo.toml (1)

4-4: LGTM!

crates/rustysnes-test-harness/Cargo.toml (1)

3-3: LGTM!

CHANGELOG.md (1)

19-55: LGTM!

docs/STATUS.md (2)

6-6: LGTM!


47-61: LGTM!

README.md (3)

17-17: LGTM!


626-633: LGTM!


664-669: LGTM!

to-dos/VERSION-PLAN.md (2)

1511-1520: LGTM!


1521-1537: LGTM!

Also applies to: 1538-1541, 1542-1553, 1554-1557

crates/rustysnes-cheevos/Cargo.toml (1)

4-4: LGTM!

crates/rustysnes-cpu/Cargo.toml (1)

3-3: LGTM!

crates/rustysnes-frontend/Cargo.toml (1)

4-4: LGTM!

crates/rustysnes-gfx-shaders/Cargo.toml (1)

4-4: LGTM!

crates/rustysnes-mobile/Cargo.toml (1)

4-4: LGTM!

crates/rustysnes-monetization/Cargo.toml (1)

4-4: LGTM!

crates/rustysnes-ppu/Cargo.toml (1)

3-3: LGTM!

crates/rustysnes-core/Cargo.toml (1)

3-3: 🎯 Functional Correctness

No dependency version updates needed. The version = "1.4.0" path requirements already accept 1.25.0, so these package bumps do not require matching dependency bumps.

			> Likely an incorrect or invalid review comment.

Comment thread to-dos/VERSION-PLAN.md
Comment on lines +1504 to +1509
### `v1.25.0 "Workbench"` — the frontend catches up with the engine — **RELEASED 2026-07-30**

Ten reviewed increments (`T-FP-A` … `T-FP-G2`, one PR each) taking the egui shell from "boots games"
to a tool you can debug, profile, record, retime, and author a TAS with. **No emulation behaviour
changes**: additive and default-off or default-inert throughout, an existing `config.toml` renders
and sounds byte-identically, and save-state `FORMAT_VERSION` stays at 10.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use one count for the Workbench increment inventory.

The release text says “ten” while the named increments are T-FP-A, T-FP-B, T-FP-C1, T-FP-C2, T-FP-D, T-FP-E, T-FP-F, T-FP-G1, and T-FP-G2—nine labels. Update every occurrence to nine or add the missing increment.

  • to-dos/VERSION-PLAN.md#L1504-L1509: correct the introductory count to match the nine detailed labels.
  • CHANGELOG.md#L14-L17: correct the release summary count.
  • docs/STATUS.md#L44-L46: correct the release-policy count.
  • README.md#L670-L673: correct the roadmap count.

As per path instructions, docs are the spec and claims must match the documented implementation.

📍 Affects 4 files
  • to-dos/VERSION-PLAN.md#L1504-L1509 (this comment)
  • CHANGELOG.md#L14-L17
  • docs/STATUS.md#L44-L46
  • README.md#L670-L673
🤖 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 `@to-dos/VERSION-PLAN.md` around lines 1504 - 1509, Update the Workbench
increment count from ten to nine to match the nine documented labels. Apply this
correction in to-dos/VERSION-PLAN.md lines 1504-1509, CHANGELOG.md lines 14-17,
docs/STATUS.md lines 44-46, and README.md lines 670-673; make no other
documentation changes.

Source: Path instructions

…iden

Review fix for #272. The root-anchored patterns miss a capture written from a
subdirectory, which is the common case for a test runner or a `cd`-ed shell.

Each pattern's breadth is now set from what is actually COMMITTED rather than
from caution:

- Zero tracked `.y4m`, `.wav`, `.slangp`, `.cgp`, `.sym`, or `rustysnes-perf-*.csv`
  files exist, so all six widen to recursive `**/` safely.
- Screenshots cannot. The repo tracks **712** PNGs, 30 of them `rustysnes-*.png`
  (the branding banners), and
  `assets/RustySNES_Icon_Pack/transparent/icon/rustysnes-icon-transparent-1024.png`
  matches even a generic `*-NNNN.png`. So the split is by precision, not by
  timidity: `screenshot::file_stem`'s default-title form
  (`rustysnes-` + exactly four digits + `.png`) has zero tracked collisions and
  goes recursive; the ROM-titled form `<title>-NNNN.png` stays root-anchored,
  because recursive it would ignore that icon.
- `**/captures/` added alongside `/captures/` — pointing the emulator at a
  captures directory makes none of the above matter, which is the outcome the
  review's "scope strictly to /captures/" suggestion is really after.

Verified both directions rather than asserted:
`git ls-files | git check-ignore --stdin -v` reports NOTHING, i.e. no tracked
file became ignored; and `captures/x.y4m`, `sub/dir/out.wav`,
`rustysnes-perf-3.csv`, `rustysnes-0007.png`, `SUPER_MARIO-0001.png`,
`deep/nest/rustysnes-0002.png`, `my.slangp` are all ignored.

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

Copy link
Copy Markdown
Owner Author

Review response — 1 fix landed in 3d9a448, 1 verification, 1 noted

No blocking issues raised. Both suggestions were worth acting on; one changed the code, one is answered with evidence.

Suggestion 1 — root-anchored ignores miss subdirectory captures: fixed

Correct, and the common case is exactly the one named: a test runner or a cd-ed shell writes below the root and the anchored pattern never fires.

The reason the patterns were anchored in the first place was fear of swallowing committed assets — so rather than trade one guess for another, I checked each extension against what is actually tracked:

pattern tracked files matching resolution
*.y4m 0 **/*.y4m
*.wav 0 **/*.wav
rustysnes-perf-*.csv 0 **/rustysnes-perf-*.csv
*.slangp / *.cgp / *.sym 0 **/ each
screenshots 712 PNGs tracked, 30 named rustysnes-*.png split — see below

Screenshots are the one place caution was justified, and the evidence is specific: assets/RustySNES_Icon_Pack/transparent/icon/rustysnes-icon-transparent-1024.png matches even a generic *-NNNN.png, and the 30 branding banners match rustysnes-*.png. So the split is now by precision rather than by timidity:

  • screenshot::file_stem's default-title form is rustysnes- + exactly four digits + .pngzero tracked collisions → recursive.
  • The ROM-titled form <title>-NNNN.png stays root-anchored, because recursive it would ignore that icon.

**/captures/ was also added next to /captures/ — pointing the emulator at a captures directory makes none of the above load-bearing, which is what "scope strictly to /captures/" is really after. I kept the filename patterns as well, since the default output path is the user's data directory and not everyone will redirect it.

Verified in both directions rather than asserted:

$ git ls-files | git check-ignore --stdin -v      # nothing — no tracked file became ignored
$ git check-ignore captures/x.y4m sub/dir/out.wav rustysnes-perf-3.csv \
      rustysnes-0007.png SUPER_MARIO-0001.png deep/nest/rustysnes-0002.png my.slangp
  → all IGNORED

Suggestion 2 — confirm the constituent PRs are merged before tagging: verified

Right thing to ask of a docs-only release commit, since the notes describe code that is not in this diff. All ten are merge-commit ancestors of this branch:

PR merge commit PR merge commit
#260 6810b81b #265 8f0ea41e
#271 bb919a0c #266 c132b7c7
#262 ed20f20b #267 7539f548
#263 43735043 #268 0e9c8c2c
#264 91412c73 #269 4060ba4a

Each checked with git merge-base --is-ancestor <merge-sha> HEAD. The two specific core fixes the notes call out are present in the tree on this branch: Cpu::interrupts_taken (3 references in rustysnes-cpu/src/lib.rs, 4 in rustysnes-core/src/scheduler.rs) and delta::Chain::evict_front. And the gate was run on this branch, not inherited: 804 tests passing, workspace clippy, per-feature clippy × 8, no_std, fmt, doc.

Nitpick — tag published with the merge

That is the plan: merge, then immediately git tag v1.25.0 and publish the release with full notes. Worth noting the badge itself does not break in the interim — it is a static shields.io text badge linking to /releases, which exists regardless — but the tag should not lag the merge and won't.

@github-actions

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR performs release maintenance for cutting v1.25.0 "Workbench", bumping crate version strings across the workspace, updating the changelog and documentation, and expanding .gitignore patterns for telemetry and captures.

Blocking issues

None found.

Suggestions

  • .gitignore#L261-L262: /captures/ is redundant since **/captures/ matches the captures directory at the repository root as well as subdirectories.

Nitpicks

  • .gitignore#L258: /*-[0-9][0-9][0-9][0-9].png requires a literal hyphen before the four digits; any capture with a custom file stem format lacking that hypen separator will bypass the root ignore pattern.

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

Review fix for #272. `**/captures/` already matches the repo root — `**/`
expands to zero or more directories — so the separate `/captures/` line ignored
nothing the pattern above it did not.

Verified in a scratch repo containing only `**/captures/`: both `captures/a.txt`
and `sub/captures/b.txt` are ignored. Re-checked here after the removal, plus
`git ls-files | git check-ignore --stdin` still reports no tracked file ignored.

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

Copy link
Copy Markdown
Owner Author

Review response (round 2) — 1 fix in d1500fc, 1 nitpick not real

No blocking issues.

Suggestion — /captures/ redundant: correct, removed

Right. In gitignore syntax **/ expands to zero or more directories, so **/captures/ already matches the repo root; the separate /captures/ line ignored nothing the pattern above it did not.

Verified rather than assumed — a scratch repo containing only **/captures/:

captures/a.txt         -> IGNORED
sub/captures/b.txt     -> IGNORED

Re-checked after the removal in this repo (captures/x.y4m and sub/deep/captures/y.wav both still ignored), and git ls-files | git check-ignore --stdin still reports no tracked file ignored.

Nitpick — a "custom file stem lacking the hyphen": not reachable

The concern would be right if the stem format were configurable, but it is not. screenshot::file_stem has exactly one return path and the hyphen is unconditional:

pub fn file_stem(rom_title: Option<&str>, index: u32) -> String {
    let base = rom_title.map(sanitize).filter(|s| !s.is_empty())
        .unwrap_or_else(|| "rustysnes".to_string());
    format!("{base}-{index:04}")
}

rom_title varies; the -{index:04} suffix does not, and it is the only .png path the screenshot code constructs (dir.join(format!("{}.png", file_stem(...)))). The other .png writes in the frontend are hd_pack.rs test fixtures under a temp dir. So there is no stem shape that reaches disk without the hyphen — and if one were ever added, the pattern would need revisiting anyway, since the four-digit counter is equally load-bearing.

Worth noting the deliberate asymmetry that remains: this pattern stays root-anchored while its sibling **/rustysnes-[0-9][0-9][0-9][0-9].png is recursive. That is not an oversight — assets/RustySNES_Icon_Pack/transparent/icon/rustysnes-icon-transparent-1024.png is a tracked file matching the generic *-NNNN.png shape, so a recursive version would ignore a real asset.

@github-actions

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR bumps workspace crate versions to v1.25.0 and updates project documentation, changelogs, release plan entries, and output .gitignore rules for the "Workbench" release.

Blocking issues

None found.

Suggestions

  • Cargo.toml: Migrate member crates to use version.workspace = true (as done in crates/rustysnes-core/Cargo.toml:3 for edition.workspace = true). Currently, 16 individual Cargo.toml files manually specify version = "1.25.0", creating unnecessary boilerplate churn across all workspace manifests for every release bump.

Nitpicks

  • .gitignore:259: The ROM-titled screenshot pattern /*-[0-9][0-9][0-9][0-9].png is root-anchored. If the emulator binary is run with its current working directory set to a subfolder of the checkout, captures matching this pattern will not be ignored unless saved in a captures/ directory.

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

… --arg

Regression I introduced in #270 and did not verify. `--arg`/`--argjson` are
**jq** flags; `gh api` has no such options and rejects them. So the delete
pipeline errored on every run, `2>/dev/null` swallowed the message, and with
`set -o pipefail` the non-zero status propagated and `set -e` killed the script
— AFTER the review comment had already been posted.

Two visible consequences, both confirmed:

- The `review` job has gone red on every run since #270 merged. Every run before
  it succeeded; every run after posted its comment and then exited 1.
- Stale review comments stopped being deleted. PR #272 accumulated **three** bot
  comments where the design is one, which is the observable proof the delete step
  had been dead rather than merely noisy.

The fix fetches the JSON raw and pipes it into a real `jq`, which does take
`--arg`/`--argjson`. (`--paginate` without `--jq` emits one array per page; jq
reads that stream fine, applying `.[]` per input.) The ids are captured first and
the loop reads from the captured value, so a listing failure degrades to
"leave them in place" with a logged warning instead of taking the script down.

stderr is no longer discarded on that call. Swallowing it is what made a
guaranteed-failing command look like a mysterious exit code, and cost the
diagnosis more than the noise would have.

The script now ends with an explicit `exit 0`, so a stray non-zero from the
delete loop can never again be reported as "the review failed" once the comment
is published.

Verified: `bash -n` clean; `shellcheck -S warning` shows only the pre-existing
SC1007 on line 138; and the exact pipeline shape, run under `set -euo pipefail`
against #272's real comment list, exits 0 and selects precisely the two stale
comments while correctly excluding the newest.

This lands on the release branch because it is my regression currently making a
check red on this very PR; holding the release for a separate round-trip would
add delay without adding safety.

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

Copy link
Copy Markdown
Owner Author

Correction: the red review check is my regression from #270, now fixed in d5c8886

Reporting this plainly rather than merging past it, because it is mine and it was silently doing damage.

What happened

In #270 I applied the reviewer's own suggestion to pass the marker via --arg/--argjson instead of interpolating it into the jq filter. The suggestion was right about the hazard; I was wrong about the mechanism. --arg/--argjson are jq flags — gh api has no such options and rejects them:

$ gh api "repos/.../comments" --arg marker "x" --jq '.[].id' ; echo $?
1

So the delete pipeline failed on every run. 2>/dev/null swallowed the error message, set -o pipefail propagated the non-zero status, and set -e killed the script — after the comment had already been posted. Hence a job that posts a perfectly good review and then reports failure, with nothing in the log explaining why.

The evidence it was real, not cosmetic

review runs result
every run before #270 merged (a7cb807, 2c046e7, 6c5395d, 6c814a5, 1f0f670, 17cabc5, f4213e0, …) success
every run after (3d9a448, d1500fc) failure — comment posted, then exit 1

And the functional damage: this PR accumulated three bot review comments where the design is exactly one. That is the delete step having been dead since #270, not merely noisy.

The fix

Fetch the JSON raw and pipe it into a real jq, which does take those flags. --paginate without --jq emits one array per page and jq reads that stream fine. The ids are captured first, so a listing failure degrades to "leave them in place" with a warning rather than taking the script down. The script now ends with an explicit exit 0 so a stray status from the delete loop can never again be reported as a failed review.

stderr is no longer discarded on that call. Swallowing it is precisely what turned a guaranteed-failing command into a mysterious exit code, and it cost more in diagnosis than the noise ever would have.

Verified: bash -n clean; shellcheck -S warning shows only the pre-existing SC1007 on line 138; and the exact pipeline shape, run under set -euo pipefail against this PR's real comment list, exits 0 and selects precisely the two stale comments (5136854716, 5136902785) while correctly excluding the newest.

Why it is on the release branch

It is my regression, currently making a check red on this very PR. Holding the release for a separate PR round-trip would add delay without adding safety. It is a self-contained commit and described as such.

On merging

review lives in a separate workflow and is not a required check — ci-success is the only one, and it gates on changes, setup, lint, test-light, full-test, no_std, bench, accuracysnes. But I would not have merged past a red check on the strength of that alone without first establishing what it was, which is what the above is.

@github-actions

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR cuts the v1.25.0 "Workbench" release by bumping workspace package versions to 1.25.0, updating documentation and changelogs, expanding .gitignore for frontend capture output, and refactoring GitHub Action comment cleanup in scripts/agy-review.sh.

Blocking issues

None found.

Suggestions

  • scripts/agy-review.sh:587: Passing --argjson new_id "$new_comment_id" to jq will fail with a JSON parse error if $new_comment_id is empty or non-numeric. Use --arg new_id "$new_comment_id" and compare .id | tostring != $new_id to prevent jq invocation failures if comment creation output is missing or malformed.
  • scripts/agy-review.sh:592-598: Feeding $stale_ids into while read using a herestring (<<< "$stale_ids") appends a newline when $stale_ids is empty, causing an unnecessary loop iteration. Guard the loop with [ -n "$stale_ids" ] before executing while.
  • .gitignore:258-259: The ROM-titled screenshot pattern /*-[0-9][0-9][0-9][0-9].png is strictly root-anchored and fixed to 4 digits. Captures generated when executing from a sub-directory or after sequence index 9999 will not be ignored.

Nitpicks

  • .gitignore:270: /presets/ is root-anchored whereas **/captures/ is recursive; consider standardizing path scoping across user asset directories.
  • docs/STATUS.md & to-dos/VERSION-PLAN.md: Release notes reference engine/CPU fixes (Cpu::interrupts_taken and delta::Chain eviction fix) in v1.25.0, but this release PR diff contains no code changes under crates/rustysnes-cpu/crates/rustysnes-core or updates to docs/cpu.md.

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

@doublegate

Copy link
Copy Markdown
Owner Author

Correction to my previous comment: review cannot go green on this PR, by design

I said the fix in d5c8886 would clear the red check. It will not, and I should have checked the workflow before saying so.

antigravity-review.yml deliberately checks out the default branch, not the PR head:

- uses: actions/checkout@v7
  with:
    ref: ${{ github.event.repository.default_branch }}

with the rationale stated right above the trigger: "the reviewer always runs from the default branch, and the PR only supplies the diff (data fed to agy), never the reviewer code." That is the correct security posture — a PR must not be able to supply the code that reviews it — and it means the review job on this PR executes main's copy of scripts/agy-review.sh, which still contains the broken line:

$ git show origin/main:scripts/agy-review.sh | grep -n 'arg marker'
578:      --arg marker "$MARKER" --argjson new_id "$new_comment_id" \

So the check stays red on #272 no matter what this branch contains, and the duplicate comments keep accumulating here (now four). The fix takes effect on the next PR after this one merges.

Where that leaves the merge

Unchanged in substance, but the reasoning is now different from what I wrote before — it is not "the fix will make it green", it is:

  • The diagnosis is confirmed against main's actual file, and the repair is verified in isolation: the exact pipeline, run under set -euo pipefail against this PR's real comment list, exits 0 and selects precisely the stale comments while excluding the newest.
  • review is not a required check. ci-success is the only one, gating on changes, setup, lint, test-light, full-test, no_std, bench, accuracysnes.
  • Merging is what deploys the fix, since the reviewer runs from main.

The one honest caveat: because the reviewer runs from main, this repair cannot be exercised end-to-end until it is merged. It has been verified as far as it can be from here — bash -n, shellcheck, and a live dry-run of the pipeline against real API data — but the first real proof will be the next PR's review job, and I will confirm it there rather than assume it.

@doublegate
doublegate merged commit 749444e into main Jul 30, 2026
15 of 16 checks passed
@doublegate
doublegate deleted the release/v1.25.0 branch July 30, 2026 22:57
doublegate added a commit that referenced this pull request Jul 30, 2026
…ice (#273)

* test(reviewer): guard the comment-selection filter that has broken twice

`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>

* test(reviewer): make the self-test's own extraction fail loudly, not 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>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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