Repository navigation
v3.0.1 "Mortar": the open items, Rust 1.99 everywhere, the review sweep, the roadmap to v4.0.0 - #590
Conversation
Famicom Yarou Vol.1 7-in-1 (mapper 45, 256 KiB PRG, CHR-RAM) booted to
noise in every release that staged it. The v3.0.0 trace showed why: the
menu writes all 8 KiB of pattern data through $2007 while every MMC3 CHR
register is 0, then draws with R0-R5 = 0, 2, 4, 5, 6, 7. Under MMC3 CHR
banking that upload lands in 1 KiB banks 0-1 only, and the outer CHR-OR
can move it further, so banks 2-7 are never written and the screen shows
whatever the RAM powered on with.
The document. The mapper 45 page says nothing about CHR-RAM, which is why
v3.0.0 left the ticket open ("fix only from a document or a hardware
measurement"). The answer is on the page of a GA23C variant: NES 2.0
mapper 372 is "INES Mapper 045 but with one bit of outer bank register #2
working as a CHR-ROM/RAM switch", and its bit table reads "Select CHR-ROM
(0)/CHR-RAM (1, unbanked)". That is the family's own CHR-RAM wiring, and
it is exactly what the trace needs. (Mapper 356, a second GA23C variant,
gives its CHR-RAM as a plain 8 KiB.) A NESdev forum post on MMC3 clones
names the two wirings boards use -- RAM A10-A12 on PPU A10-A12
("unbanked") or on the MMC3's CHR A10-A12 ("banked") -- and mapper 45
carries no submapper to choose between them, so the family document
decides.
The change: in Mmc3Board::chr_target, a Board::M45 with chr_is_ram returns
Chr::Rom(addr & 0x1FFF), the whole-image CHR-RAM indexed by the PPU
address. The existing write path already stores Chr::Rom offsets into
CHR-RAM when chr_is_ram, so reads and writes agree. CHR-ROM boards take
the unchanged arm below it.
Verification:
- m45_chr_ram_is_unbanked (new) replays the traced sequence: CHR
registers 0, 8 KiB written, R0-R5 = 0,2,4,5,6,7, outer registers set to
CHR-OR $10 / CHR-AND $7, and every byte read back. It FAILED before the
fix ($0000 read 7: register 0 banked it elsewhere) and passes after;
removing the new arm is that same failure, so the mutant is caught.
- rustynes-mappers: 954 passed, 0 failed.
- external_coverage on the five local mapper 45 dumps: only Vol.1 moved.
Its new frame is the "7 IN 1" title scene (trees, birds, sun, a girl in
green); the old committed screenshot was noise. Snapshot re-blessed and
screenshot replaced; all five then pass. The other four carry CHR-ROM
and cannot reach the new arm.
EMULATION_EPOCH 1 -> 2. ADR 0045's mechanical trigger is a moved
snapshot, and Vol.1's moved: a v3.0.0 movie of it would replay on
different pattern data, and a v3.0.0/v3.0.1 netplay pair would desync.
Both are now refused, naming both epochs. Everything that tests the epoch
is relative to the constant (movie.rs, bk2_interop.rs), so core and
netplay pass unchanged (251 + 101 + the rest, 0 failed). The constant's
doc now carries a table of which release moved it and why.
Docs in the same change: docs/mappers.md row 45, the ROADMAP ticket
(FIXED v3.0.1 with the source), CHANGELOG [Unreleased].
Not established: no hardware has been measured. The fix rests on the
372 page describing the family plus a trace that requires it; a mapper 45
board wired "banked" would need a submapper the format does not have.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…every dependency at its newest v3.0.1's dependency and toolchain refresh (the maintainer's request: every version bump, PR #584 included, except the parts that must stay on 1.96). THE TOOLCHAIN SPLIT. rust-toolchain.toml moves 1.96.0 -> 1.99.0 (newest stable, 2026-10-01). 1.100 is the current beta and 1.101 nightly, so 1.99 is the newest that is released. The pin had been held at 1.96 since 2026-09-28 for one reason: libretro's build image injects `-Car=...` into every Apple job, and `-C ar` became a hard error in Rust 1.97. That reason still stands, so the buildbot alone stays on 1.96.0: - .gitlab-ci.yml `.core-defs` sets RUSTUP_TOOLCHAIN: "1.96.0". The variable outranks rust-toolchain.toml, and every job's `rustup target add` became `rustup toolchain install ${RUSTUP_TOOLCHAIN} --profile minimal --target ${RUST_TARGET}`. The shared libretro templates set neither RUSTUP_TOOLCHAIN nor rustflags (checked in rust-apple/linux-x64/windows-x64/android-jni/ webos .yml). - The seven crates that core builds (cpu, ppu, apu, mappers, core, gamedb, libretro) keep rust-version = "1.96"; every other crate is 1.99. - CI's libretro-cross job reads the version out of .gitlab-ci.yml with a table-scoped, fail-closed awk (the same discipline as rust-setup's resolver) and builds on it, so .github/ still carries no toolchain literal and the 1.96 floor is checked on every PR rather than found on the buildbot. That check has already paid for itself. clippy 1.99 rewrote `for b in self.ram.iter_mut()` (a Box<[u8; 2048]>) to `for b in &mut self.ram`, which only 1.97+ accepts (`&mut Box<[T; N]>` is not IntoIterator on 1.96); clippy did not honour the crate's rust-version, and only the 1.96 build caught it. Fixed as `&mut *self.ram`, with the reason at the site. The RetroAchievements question (whether newer Rust eases the C bridge): 1.99 stabilises `extern "C"` variadic DEFINITIONS (VaList). None of the rcheevos functions RustyNES calls is variadic, and the one C file, static_asserts.c, exists only to report C sizeof values for an ABI test. So nothing there gets simpler; recorded rather than invented. CLIPPY 1.99. 28 findings in the first pass grew to about 70 once each crate compiled (a failing crate hides everything downstream; `--keep-going` was needed to see them). All are exact rewrites: `chunks_exact(N)` -> `as_chunks::<N>()` (stable since 1.88, so fine on 1.96 too), `fill`, `unwrap_or`, byte-string literals, a redundant #[must_use], an array destructure, and 34 `assert!(x.is_empty())` -> `assert_eq!(x, [] as [T; 0])` (clippy::assert_is_empty), applied from clippy's own suggestions and made crate-absolute where it emitted module-relative paths. `cargo clippy --fix` compiles one feature set at a time, so every CI combination was re-run after. DEPENDENCIES. Cargo: `cargo update` in both workspaces (23 lock changes; the cosim lock only libc). The only crates not at their newest are held upstream: getrandom 0.2 and 0.3 by piccolo 0.3.3 (its newest release) via rand 0.8 and ahash, and generic-array 0.14.7 by crypto-common 0.1.7's `=0.14.7`. The MSRV-aware resolver held nothing back (with and without it, `cargo update` chose identical versions). - GitHub Actions: taiki-e/install-action 2.87.21 -> 2.87.26 (this supersedes Dependabot's PR #584, which proposed 2.87.22); dtolnay/rust-toolchain at its current v1 (7e38f4b4, adds install retries). Every other action was already on its newest major. - macOS: macos-14 -> macos-15 in ci.yml and release.yml. The 14 image is in deprecation brownouts since 2026-10-05 and unsupported from 2026-11-02 (actions/runner-images 13518), still aarch64. - Android: cargo-ndk 3 -> 4 (our flags -t/-o/--platform are unchanged in 4.x), NDK r29 -> r30 installed by sdkmanager in each job (the runner image ships r29; the version is one workflow env value), Gradle run on Temurin 17 -> 25 (bytecode stays JVM 17, AGP 9.4's floor), org.json 20250517 -> 20260814. AGP, Gradle, Kotlin, the Compose BOM and every AndroidX library were already newest. - Web: wasm-opt pinned at version_133. It was not pinned at all, so trunk 0.21.14 used its built-in version_123, and web.yml's header claimed both tools were pinned; corrected. MkDocs on Python 3.14. - Docker: the signaling image on rust:1.99-trixie (it said 1.86, two years stale; the build was right only because rustup reads the copied rust-toolchain.toml anyway), runtime debian:trixie-slim, raproxy on python:3.14-slim. - pre-commit: ruff 0.16.10. Verified, on this tree: - cargo fmt (workspace + cosim) clean; all 18 CI clippy combinations rc=0 on 1.99, plus scripts/ios-host-typecheck.sh; rustdoc -D warnings; no_std thumbv7em build. - On 1.96.0: `cargo check --release -p rustynes-libretro` and the no_std chip-stack build. - cargo test --workspace + the cosim crate on 1.99: 2,926 passed, 0 failed. - Android: the CI cross-build command (cargo-ndk 4.1.2, NDK r30) and `gradlew assembleFossDebug testFossDebugUnitTest` on JDK 27, newer than CI's 25 (Gradle 9.8 supports both). - Web: `trunk build --release` downloaded and ran wasm-opt version_133. - Docs: `mkdocs build --strict` on Python 3.14.7. - Docker: both images build. Not verified locally: the libretro buildbot itself (no pipeline runs before main moves; the 1.96 build of its crates is the PR-time stand-in), the iOS Xcode build, and the GitHub-hosted macos-15 and Windows legs (CI runs them). Not done, with the reason: Xcode stays on the newest Xcode_26* the runner image has (Xcode 27 shipped 2026-09-14 but is not on macos-26 yet); targetSdk stays 36 (the Play mandate; raising it is a behaviour change); markdownlint-cli is already newest; Quartus stays 17.0.2 (MiSTer's flow). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…hat it found to-dos/plans/v3.0.1-plan.md records the maintainer's five-part scope for the first patch on v3.0.0 (the palette A/B, T-GA23C-CHRRAM, the odd-frame A12 stimulus, every dependency and toolchain bump, and every unanswered bot review comment back to #1), a measurable gate for each, and the outcome so far. The file has no codename yet; the maintainer chooses it at the cut. to-dos/mister/TASKS.md: the odd-frame stimulus item is DONE (sibling fdb48ec). Its two predictions are corrected openly rather than overwritten: the window is pre-render dots 333-336, not 337-339 (rendering takes effect a few dots after the write, so a write at 337-339 usually lands too late for the skip), and the bitstream does change, because fixing the dot-0 rule is RTL. A new open item records the one finding the bot sweep left UNSURE: whether a $2006 copy landing on a v_pipeline load dot matches the oracle (ordering confirmed, divergence unmeasured, needs a dot-257 or increment-dot stimulus). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
The maintainer chose the codename (2026-10-06); the plan file takes it, as every earlier plan does (to-dos/plans/vX.Y.Z-<codename>-plan.md). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
The release-branch commits were re-made to add the session attribution trailers the first ones lacked (unpushed, re-signed, trees unchanged), so the hashes the plan quoted no longer existed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…d of holding it Two defects in .github/dependabot.yml, both still present on v3.0.0. Doubled scope. The cargo, gradle and cosim-cargo entries set `prefix: "chore(deps)"` together with `include: "scope"`. Dependabot appends the dependency-type scope (`deps` / `deps-dev`) to the prefix itself (GitHub's dependabot options reference: "prefix followed by the type of dependencies updated"), so every Dependabot PR was titled `chore(deps)(deps): ...` (#565-#569 most recently). The three prefixes are now `chore`; `include: "scope"` supplies `(deps)` once. The github-actions entry (`chore(ci)`, no include) was correct and is unchanged. Stale hold. The root cargo entry still ignored egui, egui-wgpu and egui-winit `>=0.36` and wgpu and naga `>=30`, with a comment saying the tier was held at 0.35. The workspace moved to egui 0.36 / wgpu 30 on 2026-09-28 (vendored egui-winit; Cargo.toml lines 167-205, Cargo.lock egui 0.36.2, wgpu 30.0.1), so the ignore block was silently suppressing every future egui and wgpu release. The coupling it protected is real (Cargo.toml: two wgpu majors break every device/queue handoff, and naga is a direct frontend dependency that must match wgpu), so the five entries become an `egui-wgpu-tier` group that bumps them in one PR. It is listed first and has no `update-types`, because Dependabot puts a dependency in the first group it matches; below the minor/patch catch-all groups, a minor bump of one member would land alone. The comment records the hold as dated history and points at the vendored egui-winit, which a bump past 0.36.2 bypasses. Verified: `pre-commit run check-yaml` passes; validated against the SchemaStore dependabot-2.0 schema with jsonschema. The only schema error is the pre-existing `reviewers` key (also on HEAD; not touched here); with it removed both HEAD and this file validate clean. Bot findings: RustyNES#390/A/5314720292/4, RustyNES#391/A/5314746008/3, RustyNES#541/A/5758822410/2, RustyNES#543/A/5758833866/1 (doubled scope); RustyNES#397/A/5323299625/1 (stale egui/wgpu hold). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…put to stdout
Two scripts, both still defective on v3.0.0.
scripts/pr-review/list_unresolved_threads.py is the working list of the
bot-review closeout, and its last line, "0 unresolved thread(s)", is
what lets a merge go ahead. It read the thread list as
`(pr.get("reviewThreads") or {}).get("nodes") or []`, so a payload with
no `reviewThreads`, or a null `nodes`, printed exactly that all-clear
and exited 0 -- a fail-open gate. A partial node died on a bare
KeyError (`isResolved`, `comments`, `databaseId`) with a traceback that
hid which node and field were missing. The script now requires
`reviewThreads.nodes` to be a list and validates each node's boolean
`isResolved`, its `comments.nodes` list and the first comment's
`databaseId`, exiting through SystemExit with a message that names the
node. An empty list is still a genuine all-clear.
New scripts/pr-review/list_unresolved_threads_selftest.py runs the
REAL script as a subprocess with stdin payloads (the reply_and_resolve
selftest's precedent: test the script, not a copy of its rules). Each
refusal is asserted for its stated reason and with no traceback. Red
first: against the v3.0.0 script 5 of 7 checks FAIL (missing list, null
list, node without isResolved, without comments.nodes, comment without
databaseId); against this one, 7/7 pass. Listed in the directory README.
scripts/diag/ppu2002_read_value_histogram.py had its INPUT moved off a
predictable shared-/tmp path earlier, but still wrote its report to the
fixed `/tmp/RustyNES/an_out.txt` with a plain `open(..., 'w')`: no
private directory, no exclusive or no-follow open, so a pre-planted
symlink redirects the write. Running it on a 4-row fixture created
that file (644, 394 B). Nothing in the repository reads the file, so the
report now goes to stdout; after the change the same run prints the
report and no file is created.
Gates: ruff-check and markdownlint (pre-commit) pass on the changed
files; reply_and_resolve_selftest.py still passes.
Bot findings: RustyNES#325/B/4766773317/scripts/pr-review/list_unresolved_threads.py:49-69:174,
RustyNES#325/B/4766656411/scripts/diag/ppu2002_read_value_histogram.py:40-51:173.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…rry it tests/roms/assorted/README.md said every upstream path was in the "blargg's NES test ROMs" section of tests/roms/LICENSES.md. That section carries only the CPU, branch-timing, OAM, reset and APU ROMs; the two palette ROMs and assorted/nestest.nes (kevtris) are rows of the "full palette" ROMs section (LICENSES.md lines 193-199). The README now names both sections and which files each carries. markdownlint passes. Bot finding: RustyNES#325/B/4766656411/tests/roms/assorted/README.md:28-30:168. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…IFIED scripts/perf/perf_log_check.py printed "capture VALID -- window was on screen throughout" whenever the post-warmup `present_discarded` delta was 0. The frontend writes that column through `presentation_clock.as_ref().map_or(0, ...)` (app.rs, the F16 block), so a run with no presentation clock at all (non-Wayland, or the global never bound) logs 0 on every row, and docs/performance.md already says zero "is not proof of health" for exactly that reason. The checker asserted something it never measured. The checker now reads the header's `measured_refresh_hz` (already loaded via `load_meta`). That field is set only from the presentation clock's `poll()` answer (`self.dsync.measured_hz = Some(hz)`), so any value other than `none` is evidence the counter was live. With `none` and a zero count the line reads "validity UNVERIFIED (no presentation clock)". The header is written when logging starts, so a clock that answered later is also reported unverified -- an understatement, never an overclaim. Report line only: the >1% discard-rate hard failure and the exit codes are unchanged. docs/performance.md's capture-validity section gains the fourth state. Red first, on a real capture (perf-logs/perf-flowing_palette_nes- 20261001-041210.csv: measured_refresh_hz = none, refresh_source = none, present_discarded column present): v3.0.0 printed "capture VALID -- window was on screen throughout"; now "validity UNVERIFIED (no presentation clock)", exit 0 both times. The same capture with the header changed to measured_refresh_hz = 119.991 still reads "capture VALID". The script has no selftest mechanism, so these fixture runs are the check. ruff-check and markdownlint pass. Bot finding: RustyNES#363/A/5284716625/1. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…keeps quoted stops
Four defects in scripts/release-automation/bump_release.py, all present
on v3.0.0.
1. Swallowed lockfile failure. The `cargo metadata` refresh of the
excluded crate's crates/rustynes-cosim/Cargo.lock was wrapped in
`except Exception`, printed a WARNING and carried on, so `--apply`
could exit 0 with that lockfile still on the old version. It is now
`refresh_cosim_lock()`, which catches only CalledProcessError (with
cargo's last stderr line) and FileNotFoundError (no cargo) and
returns the message; `main` prints it as an ERROR and returns 1.
Anything else is a script bug and still raises.
2. Locale-dependent encoding. Every read_text()/write_text() and the
subprocess call used the locale's encoding. Under an ASCII locale
(PYTHONCOERCECLOCALE=0 PYTHONUTF8=0 LC_ALL=C, preferred encoding
ANSI_X3.4-1968) v3.0.0's chains_needing_a_summary() raised
"UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2" on an
em-dash document. Every call now passes encoding="utf-8".
3. terminate() promised to leave a lead ending in a closing quote or
bracket after terminal punctuation alone, but tested only the last
character: v3.0.0 returns 'the core says "enough.".',
'the core rests (for now.).' and the same for curly quotes. The
closers ("')] and U+2019/U+201D) are now looked through first; a
closer with no stop inside still gets one.
4. The ANCHORS marker decode `m.encode().decode("unicode_escape")`
re-reads UTF-8 bytes as Latin-1, so a non-ASCII marker comes back
garbled ("Current release — v" -> "Current release \xe2\x80\x94 v")
and silently matches no line. Checked: all 15 markers in today's
table are ASCII with no escapes, so nothing is mis-bumped on v3.0.0;
the defect is latent but real. `unescape_marker()` encodes as Latin-1
with backslashreplace first, which round-trips non-ASCII and still
decodes the Rust escapes.
Selftest: new cases for each -- four terminate() shapes, four
unescape_marker() cases, a UTF-8 chain document written as bytes, and a
cosim refresh against a temp root with no manifest. A harness running
the v3.0.0 functions on the same inputs under the ASCII locale fails all
five non-lockfile checks (the outputs quoted in 2-4); v3.0.0 has no lockfile function
to call, its failure path being the `except Exception` itself.
`--selftest` passes under the normal locale and under the ASCII locale.
ruff-check passes.
Bot findings: RustyNES#440/A/5382668515/1 (cargo metadata swallowed),
RustyNES#440/A/5382668515/3 (no explicit UTF-8),
RustyNES#443/A/5383852799/1 (terminate() ignores closing quote/bracket).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
`release_notes_are_not_hard_wrapped` (crates/rustynes-test-harness/ tests/release_notes_render_audit.rs) failed open in two places on v3.0.0. `read_dir(...).flatten()` dropped any directory entry that returned an error, and `read_to_string(f).unwrap_or_default()` turned a read error -- including a non-UTF-8 file -- into an empty string, which has no paragraphs and therefore no findings: a pass for a file the audit never read. Each entry error and each read error now panics with the path and the error. Red first: with a probe `.github/release-notes/vzz-red-probe.md` holding the bytes `v\xff\xfe broken`, v3.0.0's test passed (2 passed). With the fix the same probe fails with "read .../vzz-red-probe.md: stream did not contain valid UTF-8"; with the probe removed both tests pass. cargo fmt --check and cargo clippy -p rustynes-test-harness --all-targets -D warnings are clean. Bot finding: RustyNES#440/A/5382668515/2. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…ly if present
Two bot-review findings on scripts/agy-review.sh, which is installed
from the antigravity-pr-review template. The same change is committed
in the template (branch fix/v301-sweep, 9acd7f2). This repository's
copy is an older install than the template; this commit changes only
these two sites and does not re-sync the rest.
1. No test for the 406 fallback matcher. GitHub refuses an oversized
diff with two different messages (over 20,000 lines, over 300
files); both must reach the local-diff fallback, and the log must
name the limit that fired. Nothing in the self-test exercised either
message. The matcher and the label are now `diff_limit_hit()`
inside a `SELFTEST-EXTRACT: diff-limit classifier` block, and the
self-test feeds it both GitHub strings plus an unrelated HTTP 404.
Behaviour is unchanged. Mutation check: narrowing the pattern to
`(lines)`, or mislabelling the files case, each makes "406, files
variant: falls back, named 300-file" FAIL.
2. API merge base used without checking it exists. If the SHA fetch
failed and `--deepen=250` succeeded without reaching the merge base,
`git merge-base ... || echo "$api_base"` used the absent object,
logged "resolved via the compare API", and the failure surfaced as
the generic "local git diff failed". The API value is now used only
when `git cat-file -e "$api_base^{commit}"` succeeds; otherwise a
specific line says the commit is not in the local clone, and the
existing "could not compute the merge base" exit follows.
Red: the new self-test against the v3.0.0 agy-review.sh stops with
"SELFTEST-EXTRACT block 'diff-limit classifier' is missing or empty".
bash scripts/agy-review-selftest.sh: all checks passed (here and in
the template). shellcheck
reports the same findings as before the change, none on these lines.
Bot findings: RustyNES#374/A/5303548954/1, RustyNES#375/A/5303660637/2.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…review GitHub removed Dependabot's `reviewers` option in 2025 and routes review assignment through CODEOWNERS instead (github.blog changelog 2025-04-29 announcing it, 2025-08-08 completing it). The key survived in two update entries here (cargo at the root, github-actions); the current options reference no longer lists it and the SchemaStore schema rejects it, which is how the v3.0.1 sweep's tooling agent noticed it while validating the egui grouping change. Nothing about who reviews changes: `.github/CODEOWNERS` opens with `* @doublegate`, so every Dependabot PR still requests the same reviewer. `assignees` is a supported option and stays. One comment at the first site records why the key is absent, so it is not "restored" later. Verified: check-yaml passes; `grep reviewers:` matches only that comment. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
… 0036 Four bot findings on the provenance record (PR #346), re-verified on v3.0.0 and fixed without removing or weakening any statement. ADR 0036 Decision 3 and docs/originality-and-provenance.md say every derived file's provenance header names its upstream file or function. Three did not: kaiser.rs and ntdec.rs said only "derived from Mesen2", and m069_sunsoft_fme7.rs named projects, not files. The citations now in the headers come only from this repository's own record, never from the upstream source: - kaiser.rs: `Waixing/Mapper253.h`, already in the section 1 row and in the pre-v2.2.5 "Ported from Mesen2 Waixing/Mapper253.h" comment (git show 0265b3b^:crates/rustynes-mappers/src/kaiser.rs:609). - ntdec.rs: `Txc/Bmc11160.h` (section 1), plus `Ntdec/NtdecTc112.h` and `Unlicensed/Mapper204.h`, which the file's own pre-v2.2.5 "Ported from Mesen2" comments named (0265b3b^ ntdec.rs:864-865, 1049-1050) and which section 1 had not recorded; the row now records them too. - m069_sunsoft_fme7.rs: Mesen2 `NesSoundMixer::GetOutputVolume` over `Sunsoft5bAudio::_volumeLut`, the functions the file's pre-v2.2.5 "Target, derived from Mesen2" comment named (0265b3b^ :115-118); section 1 now records the same. No Nestopia file is recorded anywhere, and the header says so rather than inventing one. ADR 0036's Consequences said distributors "keep those terms for those releases", asserting the old MIT/Apache grant was effective. Accepted ADRs are not rewritten: a dated amendment adopts the provenance document's neutral wording and leaves the bullet as the 2026-08-04 record. The postmortem's executive summary stated the model's decision as fact; section 5 says that reasoning is reconstructed from the result. The sentence now carries that qualifier and points to section 5. Its timeline called the transplanted code the "v2.8.0 engine stack" with no lineage label, ambiguous now that RustyNES shipped its own v2.8.0; it is labelled as the private RustyNES_v2 project's internal v2.8.0. The quoted commit subject is unchanged. Verified: cargo test -p rustynes-test-harness --test provenance_record_audit passes (header and section 1 sets agree); markdownlint passes on the changed files. Bot findings RustyNES#346/T/PRRT_kwDOQrRows6WfQMt, RustyNES#346/T/PRRT_kwDOQrRows6Wf6_-, RustyNES#346/T/PRRT_kwDOQrRows6Wf7AB, RustyNES#346/T/PRRT_kwDOQrRows6Wf7AG. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Since v2.6.7 `Cpu::handle_interrupts` freezes the NMI dispatch copy `mc_prev_need_nmi` while `skip_irq_sample && skip_irq_sample_q` (crates/rustynes-cpu/src/cpu.rs, the "taken-branch poll rule applies to NMI too" block), following nesdev's CPU_interrupts, which says *interrupts* are not polled before the third cycle of a taken branch. Three records still described the pre-v2.6.7 behaviour: - docs/cpu-6502.md's branch_delays_irq section and the rustdoc on `skip_irq_sample` both said "NMI sampling is unaffected -- the quirk is IRQ-only". Both now say the dispatch is deferred while the NMI edge latch (`mc_need_nmi`) keeps running, so an edge is never lost; the spec names `skip_irq_sample_q` and CPU snapshot version 4. - The v4 note on `CPU_SNAPSHOT_VERSION` said a restore that dropped `skip_irq_sample_q` would resume with "the NMI edge detector re-armed a cycle early". The edge latch runs every cycle; what the field gates is the dispatch copy. It now says "the NMI dispatch gate". - The published v2.6.7 release notes said "The emulation core is unchanged" and that AccuracyCoin and nestest held "by construction", while the same notes describe this CPU change and say the gates were re-run. Published notes are not rewritten: a dated "Correction (v3.0.1)" line follows the false sentence. Comment and documentation changes only; no behaviour changes. The edits to cpu.rs are outside its SH-group provenance region. Verified: RUSTDOCFLAGS="-D warnings" cargo doc -p rustynes-cpu --no-deps clean; cargo clippy -p rustynes-cpu --all-targets -D warnings clean; RUSTUP_TOOLCHAIN=1.96.0 cargo check --release -p rustynes-libretro clean; cargo test -p rustynes-test-harness --test release_notes_render_audit --test snapshot_schema_audit pass; markdownlint passes. Bot findings RustyNES#477/T/PRRT_kwDOQrRows6dnsnR, RustyNES#477/T/PRRT_kwDOQrRows6dnsnW, RustyNES#477/T/PRRT_kwDOQrRows6dnsnd. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…claims Five bot findings on the MiSTer records, re-verified on v3.0.0. - docs/mister.md "What this is, and what it is not" said in the present tense that the sibling holds "the harness at rung 0 and no RTL". It has carried RTL since v2.4.4 and holds a complete core. The sentence is now dated history (written 2026-08-20, first shipped in v2.4.2, per git log -S and git tag --contains fbf5364), and the licence paragraph's "all 57 files" is qualified as the v2.4.3 upstream-clone count, with the v2.6.6 re-verification against the vendored tree (40 HDL files, 4 GPL-3.0-or-later, 9 GPL-2.0-or-later, 0 GPL-2.0-only) cited from the sibling's licence audit. - docs/mister.md and to-dos/mister/TASKS.md named `releases/RustyNES_MiSTer-vX.Y.Z.rbf` as the committed file. Since v2.6.15 the sibling commits `releases/RustyNES_YYYYMMDD.rbf`, the only form both MiSTer parsers accept, and attaches the version-named file to the GitHub releases. Both sites now name the two files separately, as the sibling's docs/bitstream-release.md does. - docs/mister.md said all 148 gates carry "a mutation record apiece". The five blargg cpu_interrupts_v2 verdict gates v2.6.15 added have none (no rung document, and neither tb/mutate.sh nor tb/mutate_apu.sh names them; checked by grep in the sibling). The sentence now names that exception. - to-dos/mister/contribution-checklist.md cited "the 142-gate suite"; it now gives v3.0.0's ladder (199/0/1 on-die, 200/0/1 off-die, as README.md states). - The v2.6.16 plan's acceptance line gave no off-die total; it now says 147/147 on the die and 148/148 off-die (the SDRAM latency gate is N/A on the die). Verified: cargo test -p rustynes-test-harness --test contribution_checklist_audit --test mister_source_map_audit pass; markdownlint passes on the changed files. Bot findings RustyNES#434/A/5363668968/2, RustyNES#477/T/PRRT_kwDOQrRows6dnsnY, RustyNES#495/B/5119186180/README.md:686-686:266, RustyNES#495/T/PRRT_kwDOQrRows6ffGcT, RustyNES#495/T/PRRT_kwDOQrRows6ffGcX. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Five bot findings on the agent notes (PRs #529 and #534), re-verified on v3.0.0. docs/agents/tooling-traps.md recommended `git add -A && pre-commit run <hook>` as the safe way to lint changed files. `git add -A` stages every modified and untracked file, so the next commit takes unrelated work with it. The bullet now recommends staging only the intended paths (`git add -- <paths>`) or a NUL-delimited list (`git diff -z --name-only -- '*.md' | xargs -0 pre-commit run <hook> --files`), and records the old advice as superseded. It also names the condition the trailing-space hazard needs: the list must reach argv intact, which happens in zsh (no word splitting of an unquoted parameter) or when the variable is quoted; bash or sh splitting an unquoted `$FILES` drops the space. The same file said the `gh api -F body=@file` form appears in the PR-ceremony rule in review-bots.md; no file under docs/agents/ other than tooling-traps.md contains `body=@`. The rule is now stated where the pointer was: `-F body=@reply.md`, never `-F body=-`. docs/agents/review-bots.md said CodeRabbit reports `Plan: Pro Plus` and that its check "never resolves". Its comments on #583 and #589 report `Plan: Advanced`, and its status context on #589 is SUCCESS once a review is requested (gh pr view 589 --json statusCheckRollup). The bullet now dates the plan and says the context stays pending only until a review is requested. Verified: markdownlint passes on both files; gh api repos//issues/589/comments shows "Plan: Advanced". Bot findings RustyNES#529/A/5744324402/1, RustyNES#529/A/5744324402/2, RustyNES#529/A/5744324402/3, RustyNES#534/T/PRRT_kwDOQrRows6kG_Mv, RustyNES#534/T/PRRT_kwDOQrRows6kG_M4. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Seventeen bot findings plus four drift items found during adjudication, each re-verified on v3.0.0 against the code or git history. Code-vs-doc: - `Mapper::mix_audio` returns `i32` since v2.2.3 (mapper.rs); both docs/apu-2a03.md and docs/expansion-audio.md still printed `-> i16`. - docs/frontend.md described v2.2.9's embedded `show_viewport_immediate` detach with real OS windows "tracked as follow-up"; v2.3.0's detached.rs gives each panel its own OS window (docs/STATUS.md says so). The paragraph now describes detached.rs and keeps v2.2.9 as history. - ios/RustyNES/GameView.swift's comments named only Movies/TAStudio and Cheats as non-pausing; `updateMenuPaused()` pauses only for States, Settings and the Debugger, so Netplay, Achievements and Lua also keep the core running. Both comments now state the rule directly. - docs/STATUS.md: the implied dummy read covers 22 official opcodes plus the six one-byte unofficial NOPs (28 opcode values, 23 match arms calling `Cpu::implied_dummy_read`), not "23 opcodes"; and one row used `->` where its neighbour uses the arrow. - debugger/mod.rs rustdoc on `detachable_window` still showed the old `⧉` glyph and an `egui::CentralPanel`; the UI uses the Font Awesome EXPAND/COMPRESS glyphs and an `Area` + central-panel `Frame`. Counts and figures: - tests/roms/LICENSES.md: 33 AccuracyCoin sub-tests (git ls-files), not 26, twice; 338 committed `.nes`, not 328; AccuracyCoin 144/144, not 141/141. - docs/performance.md: the F19 table's mmc3 saving now prints 7.5 us, the difference of its rounded columns (the unrounded means were not recorded), with a dated note; the memcpy estimate states its ~10-20 GB/s assumption (also in benches/snapshot_restore.rs); the 5/5 sign-test p is written 1/32 = 0.03125 here and in CHANGELOG.md (the published v2.3.3 notes stay as recorded). - docs/scheduler.md quoted ~3.9/2.5 ms and ~3.8/2.6 ms for the same thing; both now give the shipped fast-path figures from docs/performance.md "Current figures" (~3.95 / ~2.65 ms). History: - CHANGELOG [2.4.1] said the release-anchor audit and backlog sweep landed "between v2.4.0 and v2.4.1"; both landed in #427 (a1dd5fd, 09:32) before v2.4.0 merged (b67c4f9, 13:20; merge-base confirms ancestry). CHANGELOG and VERSION-PLAN.md's v2.4.0 row now agree. - VERSION-PLAN.md: the v2.3.0 row no longer calls it the head of the v2.x line; the v2.6.6 row marks its +0.363/+0.245 ns slack as withdrawn at v2.6.7 with the re-measured pair; the v2.6.16 row says three passes followed by a failure at +3 are the evidence. - The v2.0.x mobile-finalization plan gains a banner marking its store line superseded by ADR 0035, and its iOS lines name the v1.9.x TestFlight train. - ROADMAP.md / OVERVIEW.md "Last Updated" set to 2026-10-06, the date both were last edited (ad32ce3, 9c23715). - SUPPORT.md called the root ROADMAP.md a pre-1.0 snapshot; it is the maintained project roadmap. Verified: cargo fmt --all --check; cargo clippy -p rustynes-core -p rustynes-frontend --all-targets -D warnings; RUSTDOCFLAGS="-D warnings" cargo doc -p rustynes-frontend --no-deps; cargo test -p rustynes-test-harness --test release_anchor_audit --test release_state_prose_audit --test feature_flag_audit pass; markdownlint passes on every changed Markdown file. Bot findings RustyNES#194/B/4568745536/to-dos/plans/v2.0.x-mobile-finalization-plan.md:30:159, RustyNES#201/B/4576269787/ios/RustyNES/GameView.swift:120:162, RustyNES#342/T/PRRT_kwDOQrRows6WKQcA, RustyNES#344/T/PRRT_kwDOQrRows6WZY5g, RustyNES#346/A/5186922238/1, RustyNES#347/T/PRRT_kwDOQrRows6Woict, RustyNES#365/A/5286477079/2, RustyNES#367/A/5287157195/1, RustyNES#372/A/5293088673/3, RustyNES#430/T/PRRT_kwDOQrRows6a8ZeA, RustyNES#439/T/PRRT_kwDOQrRows6bbFw-, RustyNES#477/T/PRRT_kwDOQrRows6dnsnk, RustyNES#495/T/PRRT_kwDOQrRows6ffGcV, RustyNES#507/A/5643265643/4, RustyNES#507/T/PRRT_kwDOQrRows6hs6U8. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…omment Found by the v3.0.1 bot-sweep docs agent and confirmed by history: v2.2.5 (0265b3b), the relicensing release, reworded the Sunsoft 5B mix-level comment in m069_sunsoft_fme7.rs from **Target, derived from Mesen2 (the project's accuracy bar) rather than from our own prior numbers.** to "**Target, calibrated against Mesen2 ... as an oracle rather than against our own prior numbers.**" -- in the same commit that added the file's `// Provenance: ... derived from Mesen2` header. The derivation was therefore never hidden (header, docs/originality-and-provenance.md section 1, NOTICE), but the comment AT the derived value described a black-box comparison, which is the wording this project reserves for genuinely independent code. Under the never-launder rule that is the wrong direction, and the maintainer chose to restore it (2026-10-06). The comment now says "derived from Mesen2", points at the header and section 1, and records that v2.2.5 had reworded it and v3.0.1 restored it, so the history is at the site rather than only in git. Checked the rest of that release for the same pattern: v2.2.5 replaced ten other "Ported from Mesen2 <file>" / "Ported from puNES <file>" site comments with NESdev-wiki wording plus "no third-party emulator code is incorporated". All ten have since been re-attributed ("the implementation is derived from Mesen2's `<file>` (GPL-3.0-or-later). See NOTICE + ... section 1"), and every cited upstream file is named in a header and in section 1. This comment was the only one left. Not changed, raised to the maintainer: rustynes-gfx-shaders/src/lib.rs:311 describes the Bisqwit-style NTSC pass as "an independent implementation ... no third-party emulator code is incorporated". That is a self-certification the provenance guardrails forbid, and the NESdev "NTSC video" page it cites carries Bisqwit's published code, so whether the pass derives from it is an open question for review, not one to settle by editing the sentence. Comment-only; cargo fmt and clippy for rustynes-mappers clean. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
The Bisqwit-style NTSC pass documents itself as independent and says no third-party emulator code is incorporated: a self-certification the provenance guardrails forbid, about a pass named after the author whose code the cited NESdev page publishes. Recorded as an open question for the maintainer, with what would settle it, rather than decided by editing the sentence. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Seven bot findings against crates/rustynes-cosim that were still valid on v3.0.0. checkpoint::from_bytes checked only that the length was a multiple of 16, so a corrupt or non-monotonic .ckpt.bin given to checkpoint_diff reached Divergence::window_len, whose `through_cycle - after` underflowed (a panic in debug, a wrapped length in release). from_bytes now refuses a stream whose through_cycle does not strictly increase (Hasher cannot produce one) and window_len uses checked_sub. Red first: the two new tests failed with "attempt to subtract with overflow" and an Ok parse of a backwards stream. (Bot finding RustyNES#435/T/PRRT_kwDOQrRows6bAg8w) nes_golden_export wrote <stem>.irq.csv before matching the checkpoint result, so an overflowed trace left a truncated CSV with no overflow marker on disk and then panicked. The CSV is now written in the Ok arm only; obs.bin keeps its documented keep-on-overflow behaviour. Red first: an_overflowed_trace_writes_no_irq_csv saw the CSV written and failed. (Bot finding RustyNES#433/T/PRRT_kwDOQrRows6a_ssH) Observable::from_cycle_record open-coded the bus-access numbering that access_code already defines; it now calls access_code, and a new test maps every BusAccess variant through both. (RustyNES#433/A/5363505721/3) localisation_is_consistent had its two `false` arm comments swapped: (Identical, Some) is the false negative that matters, (Diverged | Inconclusive, None) the false positive. (RustyNES#435/T/PRRT_kwDOQrRows6bAg82) Docs: take_irq_artifacts and take_checkpoints gain `# Panics` sections for a zero interval (RustyNES#433/A/5363505721/2); the three rn_write_* FFI writers replace "SAFETY: as above." with the invariant they rely on (RustyNES#433/T/PRRT_kwDOQrRows6a_ssT) and each now says it consumes the trace (RustyNES#436/A/5364286445/1). rn_write_irq_trace_csv's advice to write the checkpoints first was wrong, since that writer consumes the trace too; it now says so. Verified: cargo fmt --check, cargo clippy --all-targets -D warnings and cargo test, all with --manifest-path crates/rustynes-cosim/Cargo.toml. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Four low-severity bot findings, each still present on v3.0.0. None changes
emulation behaviour.
- rustynes-cpu: implied_dummy_read wrapped its single read1 call in a
bare block left over from the removed cfg branches. The braces are gone.
(Bot finding RustyNES#507/A/5643265643/1)
- rustynes-frontend config.rs: an empty line directly after
`mod tests {`. Removed. (RustyNES#414/A/5349340796/3)
- fds_trace: the headered-.fds check compared only the first three bytes
to "FDS" while the comment above it names the four-byte "FDS\x1a" magic.
It now uses `disk.starts_with(b"FDS\x1a")` with the same 16-byte length
guard, so code and comment agree. (RustyNES#39/B/4493453078/...fds_trace.rs:62:156)
- zapper_light_probe: `let bright = bright_px >= 2;` was never read and
was discarded with `let _ = bright;`; the output prints bright_px
directly. Both lines removed. (RustyNES#383/A/5309752445/8)
Verified: cargo fmt --all --check; cargo clippy -p rustynes-cpu
-p rustynes-test-harness --all-targets -D warnings; cargo test -p
rustynes-cpu; RUSTUP_TOOLCHAIN=1.96.0 cargo check --release -p
rustynes-libretro.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
parse_row read both the vendored table and the user overlay, and its `filter(|&m| m != 0)` -- correct for the vendored table, whose unfilled rows carry `0` and which has no separate empty marker -- also discarded a mapper-0 override the user saved from the ROM Database panel. The override applied in-session through upsert_user_entry, serialize_row wrote it as `0`, and the next start read it back as "no override". The overlay has an empty marker: serialize_row leaves the Mapper column blank for None. So an overlay `0` is a deliberate NROM correction. The overlay is now read by parse_overlay_row, which keeps it; the vendored table keeps its rule through parse_row. Both share parse_row_from. Red first: an_overlay_mapper_zero_override_survives_reload failed with `mapper: None` where `Some(0)` was saved. It also pins that the vendored reader still drops the `0` and that an empty overlay column stays None. Bot finding RustyNES#373/A/5303905922/3. Verified: cargo fmt --all --check; cargo test -p rustynes-gamedb (all pass); cargo clippy -p rustynes-gamedb --all-targets -D warnings; RUSTDOCFLAGS=-D warnings cargo doc -p rustynes-gamedb; RUSTUP_TOOLCHAIN= 1.96.0 cargo check --release -p rustynes-libretro. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
No unit test set the v6-tail fields (spr_halted[0..8], prev_rendering_enabled, rendering_enabled_delayed, oam_corruption_pending, oam_corruption_index, oam_corruption_disabled, oam_corruption_disabled_instant) away from their power-on defaults before a snapshot/restore. snapshot_round_trip does not touch them, and snapshot_schema_audit proves only that the writer mentions each field, so a reader that disagreed with the writer about order would still have passed: every field reads back the value it already had. The new test flips one field at a time and compares the whole tail after restore, so a swapped pair of adjacent bools reads back as two wrong fields. This is a coverage gap, not a behaviour defect, so the test is green on the current code; it was checked by mutation instead: swapping the reader lines for oam_corruption_disabled and oam_corruption_disabled_instant failed this test and no other snapshot test (26 passed, 1 failed). The mutation was reverted. Bot finding RustyNES#250/T/PRRT_kwDOQrRows6PurIn. Verified: cargo fmt --all --check; cargo clippy -p rustynes-ppu --all-targets -D warnings; cargo test -p rustynes-ppu; RUSTUP_TOOLCHAIN= 1.96.0 cargo check --release -p rustynes-libretro. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…overs
Four bot findings against rustynes-frontend, all still valid on v3.0.0.
Bisqwit index staging (medium). The two no-ROM present branches refilled
only present_staging (RGBA); present_index_staging kept the last game's
index frame, and close_rom cleared it to empty. Gfx skips the index
upload on a length mismatch and keeps the texture's previous contents,
so with the composite-rt (Bisqwit) filter on, closing the ROM left the
last game frame on screen instead of black. A new associated fn,
App::blank_present_staging, fills the RGBA half with zeros and, when an
index consumer is active, a full NES_W*NES_H index frame of colour $0F;
both no-ROM branches and close_rom now call it. Red first:
a_blank_present_stages_a_black_index_frame failed against a helper that
did only what the no-ROM branches did. (Bot findings RustyNES#33/B/
4492472013 at app.rs:4547:154 and app.rs:4620:155 -- one defect.)
Startup-path test (RustyNES#373/A/5303905922/2).
the_startup_path_applies_the_same_header_overrides_as_the_menu_path built
bytes that never matched a DB row, so it always took its early return
and never ran the comparison. The image now forges its last four hashed
bytes so the header-excluded CRC32 is Seicross's (0x0F05FF0A, mapper 185
submapper 4), the early return is gone, and it asserts the helper
changes the header and matches apply_header_overrides. Mutation check:
making apply_load_time_header_overrides a no-op fails it ("the startup
helper must rewrite the header"); reverted.
Run-ahead throttle (RustyNES#371/A/5290707031/1). The per-frame cost
model was written out separately in the engage cascade and the release
arm; both now call one private per_frame_cost, pinned by a unit test.
tick_iv (RustyNES#366/A/5287325218/1). tick_iv_ns is differenced on the
receiver, so a tick dropped on the depth-1 channel is never seen and the
next interval spans two sends, while its docs said "between successive
SENDS". The field docs now say "between delivered ticks" and that it
equals the send cadence only while tick_dropped is 0; docs/performance.md
gets a dated correction under the F15 table (drops were measured at 0 and
0-1 per capture, so the F15 conclusion stands).
Verified: cargo fmt --all --check; cargo clippy -p rustynes-frontend
--all-targets -D warnings with default, scripting, scripting,hd-pack,
retroachievements and full; cargo test -p rustynes-frontend --lib for the
three tests; pre-commit run markdownlint --files docs/performance.md.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Five bot findings against three audit tests in rustynes-test-harness,
each still valid on v3.0.0. Each audit could pass over prose or rows it
exists to check.
release_state_prose_audit: versions_near collected digits and dots
greedily, so a version ending a sentence ("v2.4.2.") read as "2.4.2.",
which triple() rejects, and the version was silently skipped -- the same
scanner feeds tag_claims. It now trims trailing dots before triple().
Red first: a_version_ending_a_sentence_is_still_found returned [] for
"Next up — v2.4.2.". The audit still passes on the current tree with the
fix, so no prose was hiding behind it. (RustyNES#438/A/5381656496/1)
release_anchor_audit: the theme check refused only `**`, `__` and a
backtick, so `~~strike~~` or a `[link](url)` would reach the plain-text
GitHub release title unrendered. The list moves into
title_markdown_marker() and gains "~~" and "]("; single `*`/`_` stay
allowed. Red first: title_markers_cover_strikethrough_and_links got None
for "~~". (RustyNES#459/A/5397645197/1)
feature_flag_audit, three findings:
- the table ended at the next blank line, so prose directly under it was
read as table text (to EOF with no blank line). It now ends at the
first line that does not start with `|`. (RustyNES#507/A/5643265643/2)
- a table line that did not parse as "| `flag` |" was skipped with
`continue`, so a malformed row vanished while the row-count floor still
passed. It now panics naming the line. (RustyNES#507/T/PRRT_kwDOQrRows6hs6Uy)
- both cross-checks pass over rows marked *(removed)*, so a manifest
re-declaring cpu-implied-dummy-reads passed every test.
no_removed_flag_is_still_declared now asserts that no removed row names
a declared flag. (RustyNES#507/T/PRRT_kwDOQrRows6hs6U2)
The parsing moved into parse_feature_table(text) so it can be tested on
synthetic tables. Red first: the v3.0.0 parse loop, compiled standalone
with the two new tests, read a stray "| `gamma`" line after the table as
a row and did not panic on "| delta |". removed_but_declared is pinned
by a synthetic test; the real-tree check passes.
Verified: cargo fmt --all --check; cargo clippy -p rustynes-test-harness
--all-targets -D warnings; cargo test -p rustynes-test-harness for the
three test files (9, 16 and 7 passed).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
… rustdoc `MAX_APU_TRACE_CAPACITY` (pub) linked `[`APU_REC_LEN`]`, a private const, which `RUSTDOCFLAGS="-D warnings" cargo doc` rejects. It went unseen because rustynes-cosim is excluded from the workspace and CI built its clippy and tests but never its rustdoc; the v3.0.1 sweep's code agent found it by running it by hand. The link is now a plain code span (the project rule for items a public doc cannot link), and the lint job gains a cosim rustdoc step beside the existing cosim clippy and test steps. Verified: cosim `cargo doc --no-deps` with -D warnings passes; actionlint passes on ci.yml. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…released] Two v3.0.1 results with no entry yet: the MiSTer core's dot-0 A12 rule now gated on cycle-0 rendering (found by the new mapper4mmc3oddskip080 stimulus, which also gates the odd-frame exception through the new /IRQ-rise comparison), and the back-to-#1 bot-review sweep (290 unanswered items, 473 verdicts, 80 still-valid findings fixed), listed by user-visible effect. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…d on it Three comments (.gitlab-ci.yml, docs/agents/libretro.md, ci.yml) said the libretro buildbot cannot run a change before `main` moves, which made the v3.0.1 toolchain split look untestable until after release. It is false. libretro's mirror runs a pipeline for every pushed branch: the v3.0.0 cycle has pipelines for release/v3.0.0 and both review slices, and the pushed release/v3.0.1 got pipeline 119606 about 20 minutes later. All 15 jobs passed on the RUSTUP_TOOLCHAIN=1.96.0 pin, including osx-x64, osx-arm64, ios-arm64 and tvos-arm64, the four that `-C ar` threatens. Recorded with two facts that make it usable: the job list is public through the GitLab API while job logs need a login (401), and a review-slice branch fails every job by construction because it holds only part of a release. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
`rustynes-gfx-shaders` documented `BISQWIT_WGSL` as "an independent
implementation of the NES composite signal model documented at the NESdev
wiki; no third-party emulator code is incorporated". That is a
self-certification, which docs/ai-emulator-provenance-guardrails.md forbids
as a finished claim. It was also false. The constant is
`include_str!("bisqwit.wgsl")`, and that file is generated verbatim from
`rustynes-frontend`'s `ntsc_bisqwit::shader_src()` (a drift test keeps the two
identical). `ntsc_bisqwit.rs` has long carried a `// Provenance:` header and a
section 1 row saying its coefficient tables were ported verbatim from
Bisqwit's C via Mesen2's `BisqwitNtscFilter` (GPL-3.0-or-later). So the
shared crate held a copy of recorded-derived code under a sentence denying
it. The maintainer's decision (2026-10-07) was to treat it as derived; the
existing record made that the only consistent answer.
Changes:
- lib.rs gains an SPDX line and a `// Provenance:` header naming
`BISQWIT_WGSL` / `bisqwit.wgsl`. The doc comment now states the derivation
and says it read "independent" until v3.0.1.
- Section 1 of docs/originality-and-provenance.md: the CRT row now names only
crt_stack.rs (lib.rs merely re-exports those shaders), and lib.rs gets its
own Bisqwit; Mesen2 row.
- NOTICE's video-filter section names the generated WGSL file.
- provenance_record_audit.rs: `ROW_WITHOUT_HEADER` is now empty. Its one
entry was lib.rs, excused as "only re-exports the CRT shaders", and that
excuse is exactly what hid the Bisqwit copy from the audit.
- to-dos/ROADMAP.md: T-NTSC-PROVENANCE closed, with the original record
kept below the resolution.
Deliberately not done: no header in bisqwit.wgsl itself. The file is
generated byte for byte from shader_src(), and a header there would fail the
drift test. The lib.rs header covers the constant that includes it.
Verified: provenance_record_audit 2/2 pass. With the new lib.rs header
mutated away it fails every_section_1_row_names_a_file_that_carries_a_header
(caught), and passes again once restored. clippy -D warnings on
rustynes-test-harness and rustynes-gfx-shaders (all targets) is clean, fmt is
clean, and markdownlint passes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
v3.0.1 had split the toolchain. The workspace moved to 1.99.0, while the libretro buildbot stayed on 1.96.0 (`RUSTUP_TOOLCHAIN` in .gitlab-ci.yml), because its build image injected `-Car=<path>,Clink-arg=...` into every Apple job and `-C ar` is a hard error from Rust 1.97. The seven crates that core compiles kept `rust-version = "1.96"`, and libretro-cross built them on 1.96. That premise had already expired. The v3.1 roadmap research found that libretro-infrastructure/libretro-build-rust removed the flag on 2026-09-03 (841f3619, "Remove -C usage as no longer supported by rust"); the rebuilt image's own pipeline passed on 2026-09-23. libretro's mirror builds every pushed branch, so this was tested before merge rather than after. test/libretro-rust-1.99 (771fdb8) changed only `RUSTUP_TOOLCHAIN` to "1.99.0". Its pipeline 119614 passed all 15 jobs, including osx-x64, osx-arm64, ios-arm64 and tvos-arm64, the four `-C ar` used to break. Maintainer decision D5 (2026-10-07): test it in v3.0.1, and drop the pin if everything is green. Changes: - .gitlab-ci.yml `.core-defs`: `RUSTUP_TOOLCHAIN: "1.99.0"`, the exact configuration 119614 tested. The variable stays because every job's `rustup toolchain install ${RUSTUP_TOOLCHAIN}` needs a name. The tvOS comment no longer calls `-C ar` a live hazard. - The seven crates (cpu, ppu, apu, mappers, core, gamedb, libretro) now use `rust-version.workspace = true` (1.99); cargo metadata confirms it. - ci.yml libretro-cross: instead of building on an older toolchain, it now FAILS if `.gitlab-ci.yml`'s RUSTUP_TOOLCHAIN differs from rust-toolchain.toml's `channel`. Both are parsed table-scoped and fail-closed. The step was extracted from the workflow YAML and run against copies: match -> rc 0, channel 1.100.0 -> rc 1 with the error, no [toolchain] channel -> rc 1. A pin move that forgets the buildbot is now caught on the PR, not on git.libretro.com after merge. - rust-toolchain.toml keeps the two-line RUSTFLAGS fix in case a future image reintroduces `-C ar`. It adds the trap the research flagged: the image now separates its `-C link-arg` flags properly, so those link arguments reach the linker for the first time. They used to be swallowed as the `ar` value. 119614 links fine, but this is the first suspect if an Apple link ever changes behaviour. - mmc3_boards.rs `mirror_bank`: three `size & size.wrapping_neg()` become `size.isolate_lowest_one()`. This is an exact rewrite (lowest set bit; `size` is never 0 because `count > 0`). Clippy's `manual_isolate_lowest_one` is MSRV-gated, so it fired only once the crate's rust-version reached 1.99. Trap worth knowing: a workspace `cargo clippy` run straight after the rust-version edit reported nothing, and only the `-p rustynes-libretro` run (a separate feature unification, so a fresh lint pass) flagged it. Clippy's cached results evidently do not key on rust-version; that is a hypothesis not tested further, and a fresh CI cache lints it either way. - Docs: AGENTS.md toolchain paragraph, docs/agents/ci-and-release.md and libretro.md (a new bullet, "AND v3.0.1 UNDID THE SPLIT"), the canary header, CHANGELOG ("Rust 1.99, everywhere"), and the roadmap records: the line plan, v3.0.1 and v3.1.0 plans, libretro SPRINT_PLAN L1 (done), VERSION-PLAN and T-LIBRETRO-TOOLCHAIN (closes on the first green `main` pipeline after merge). Verified on this tree: - clippy -D warnings for the workspace, the scripting, scripting+hd-pack, retroachievements and full frontend sets, both wasm32 sets, and -p rustynes-libretro; - rustdoc -D warnings, fmt, and the no_std thumbv7em build; - mmc3_boards tests 33/0; - release_anchor 14/0, release_state_prose 16/0, contribution_checklist 9/0; - markdownlint, actionlint and check-toml. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Moves every release anchor from 3.0.0 "Cornerstone" to 3.0.1 "Mortar":
- the workspace and cosim versions, both Cargo.locks;
- the libretro .info display_version;
- Android versionName / versionCode 30001;
- iOS MARKETING_VERSION;
- the AGENTS, README, STATUS, VERSION-PLAN, ROADMAP, OVERVIEW, ARCHITECTURE,
SECURITY and SUPPORT anchors.
CHANGELOG [3.0.1] is dated 2026-10-07, with an intro, a roadmap entry and a
Verification section. .github/release-notes/v3.0.1.md is the release body.
bump_release.py had a latent bug, found here and fixed in this cut. The
Cargo.toml anchor pattern `version = "{v}"` was unanchored, so it also matched
every internal path-dependency requirement. v3.0.0 moved those requirements
to "3.0.0", so the first cut whose OUTGOING release is an X.0.0 found 15 copies
of the string and refused. No earlier cut could see it, because the
requirements sat at "2.0.0" while the workspace moved through 2.x.y. Both
Cargo.toml patterns are now whole-line ("\nversion = ...\n"). A selftest case
builds a manifest with a matching requirement. Mutation: reverting to the
unanchored pattern makes that case FAIL (caught); the selftest passes
otherwise.
The script also refused, correctly, until to-dos/ROADMAP.md's release-line
chain got a written v3.0.1 entry (its "owed a summary" rule).
Records:
- VERSION-PLAN moves the forward v3.0.1 row into the release table, marked
(current).
- README's MiSTer paragraph and STATUS's top-line counts now carry v3.0.1's
numbers.
- The Cargo.toml comment no longer describes the one-day 1.96 split.
- The plan's outcome rows: item 5 is done; item 1, the palette A/B, is still
pending a quiet host.
Verification on the final tree (3175827 + this cut; the cut changes no
code):
- test-roms 3,234 / 0 / 11; workspace 2,886 / 0 / 7; cosim 54 / 0;
- commercial 60 / 0, 137 / 0, 6 / 0;
- fmt, 18 clippy combinations, rustdoc, no_std;
- the seven release audits (release_anchor, release_state_prose,
contribution_checklist, release_notes_render, provenance_record,
libretro_info, cosim_manifest), 0 failed (per-audit counts not quoted: they
came from one combined run, whose output order does not name the suite);
- bump_release --selftest;
- markdownlint.
MiSTer: ladders 200/0/1 on-die and 201/0/1 off-die. Seed 2 at 261007, with
byte-identical double compiles: on-die 7e81a718..., off-die 88d1dfa5....
NOT included: an uncommitted condensation of AGENTS.md (and an untracked
docs/history/ archive) written by another session at 01:55. This commit
stages AGENTS.md as HEAD plus only the anchor edits, produced by running the
bump in a clean worktree. Every other file was compared byte for byte against
that clean run. The other session's edits remain in the working tree for the
maintainer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Docs7 for doublegate/rustynes
Commit |
|
@coderabbitai review |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
AGENTS.md is loaded into every agent session, so its length is paid on every turn. Another agent session on this machine condensed it at 01:55 on 2026-10-07 and left the change uncommitted. The release cut (fa48dfc) deliberately did not carry it: that commit staged AGENTS.md as HEAD plus the version-anchor edits only. The maintainer has now reviewed the change and directed it committed (2026-10-07). What changed in AGENTS.md: - per-region firewall paragraph: the dated incident narrative is condensed; the rule, the grep command and the escalation ladder remain; - "Timebase (v2.0.0)" release bullet: removed from the current-release block; - "(51 -> 172 families)": dropped from the release-history pointer (the count was stale; 191 now); - engine-lineage, hot-path and commit-body paragraphs: condensed; - "Exhaustive Documentation Sweeps" and "GitHub Wiki Initialization" bullets: removed. Every removed or shortened passage is reproduced verbatim in the new docs/history/AGENTS-archive.md, under its original line number (8 sections), so nothing is lost and each removal can be reviewed against the original. Not committed: docs/history/CLAUDE.local-archive.md, the same session's archive of CLAUDE.local.md. It is matched by a global gitignore rule, because CLAUDE.local.md is private session state. Checks: - markdownlint on both files: passes, after trimming one trailing blank line from the archive (MD012); - release_anchor, release_state_prose, contribution_checklist and provenance_record audits: all pass on the condensed file. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Antigravity review (Gemini via Ultra)A maintenance release that establishes Rust 1.99 across the workspace, patches MMC3 timing and Mapper 45, bumps the emulation epoch, and resolves pending review items. Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by Earlier review rounds (newest first)Round reviewed at 2026-10-07 13:24 UTCAntigravity review (Gemini via Ultra)This maintenance release fixes CHR-RAM addressing for mapper 45, updates the project to Rust 1.99 along with all dependencies, resolves lingering code reviews, and lays out the roadmap to v4.0.0. Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by Round reviewed at 2026-10-07 13:04 UTCAntigravity review (Gemini via Ultra)A maintenance patch (v3.0.1) that fixes Famicom Yarou graphics, MMC3 timing in the MiSTer core, updates the workspace to Rust 1.99, resolves pending review threads, and drops the vendored TriCNES reference. Blocking issues
Suggestions
Nitpicks
Automated first-pass review by Round reviewed at 2026-10-07 11:38 UTCAntigravity review (Gemini via Ultra)Bumps the emulation epoch for mapper 45 fixes, drops vendored TriCNES golden references, enforces Rust 1.99, and fixes script failure paths. Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by Round reviewed at 2026-10-07 11:30 UTCAntigravity review (Gemini via Ultra)This PR bumps the Rust toolchain to 1.99 and updates dependencies, fixes an MMC3 timing exception, and increments the Blocking issues
Suggestions
Nitpicks
Automated first-pass review by Round reviewed at 2026-10-07 11:20 UTCAntigravity review (Gemini via Ultra)Updates Rust to 1.99 across the workspace and libretro builds, resolves deferred review items, and corrects minor testing logic defects. Blocking issues
Suggestions
NitpicksNone found. Automated first-pass review by Round reviewed at 2026-10-07 11:02 UTCAntigravity review (Gemini via Ultra)This PR (v3.0.1) updates the MSRV and all environments to Rust 1.99, fixes CHR-RAM unbanking for Mapper 45, corrects an MMC3 A12 odd-frame interrupt timing issue in the MiSTer core (with co-simulation test coverage), groups Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Several changed files incorrectly retain the removed Rust 1.96 libretro exception, and the review-thread closeout helper can still miss paginated threads.
Review effort: Balanced
Findings: 1
Open (14)
Paginate review threads in the fail-closed selftest · New Remove stale per-crate Rust 1.96 resolver comment · New Update architecture overview from Rust 1.96 to 1.99 · New Remove obsolete seven-crate Rust 1.96 exception · New Correct false Rust 1.96 compatibility comment · New Correct false Rust 1.96 compatibility comment · New Remove obsolete rustynes-core Rust 1.96 rationale · New Correct false Rust 1.96 compatibility comment · New Correct false Rust 1.96 compatibility comment · New Correct false Rust 1.96 compatibility comment · New Correct false Rust 1.96 compatibility comment · New Correct false Rust 1.96 compatibility comment · New Remove obsolete Rust 1.96 compatibility exception · New Remove obsolete split-MSRV example · New
What changed in this PR
Prepares the v3.0.1 “Mortar” maintenance release with an emulation fix, Rust 1.99 migration, provenance cleanup, dependency refreshes, and roadmap updates.
Changes:
- Fixes mapper 45 CHR-RAM behavior and raises
EMULATION_EPOCH. - Moves the workspace and release infrastructure to Rust 1.99.
- Updates provenance records, review tooling, CI, documentation, and release metadata.
| File | Description |
|---|---|
to-dos/plans/v2.6.16-interlock-plan.md |
Corrects co-simulation totals. |
to-dos/mister/contribution-checklist.md |
Updates MiSTer evidence totals. |
to-dos/libretro/IMPLEMENTATION_PLAN.md |
Marks the plan historical. |
tests/roms/assorted/README.md |
Clarifies ROM provenance. |
scripts/pr-review/README.md |
Documents the new review self-test. |
scripts/perf/perf_log_check.py |
Avoids unverified presentation claims. |
scripts/diag/ppu2002_read_value_histogram.py |
Writes reports to stdout safely. |
ios/RustyNES/GameView.swift |
Corrects sheet-pausing documentation. |
ios/README.md |
Updates the Rust prerequisite. |
ios/project.yml |
Bumps the iOS marketing version. |
docs/user-guide/getting-started.md |
Updates the required Rust version. |
docs/testing-strategy.md |
Updates the CI toolchain reference. |
docs/scheduler.md |
Corrects measured performance figures. |
docs/expansion-audio.md |
Corrects the audio hook type. |
docs/dev/STYLE_GUIDE.md |
Updates MSRV guidance. |
docs/dev/BUILD.md |
Updates build prerequisites. |
docs/cpu-6502.md |
Documents taken-branch NMI deferral. |
docs/apu-2a03.md |
Corrects the expansion-audio API. |
docs/ai-emulator-provenance-guardrails.md |
Defines the TriCNES exception. |
docs/adr/0036-relicense-gplv3-derivative-work.md |
Clarifies historical licensing terms. |
docs/adr/0035-rustynes-is-permanently-non-commercial.md |
Schedules free store listings. |
docs/adr/0032-vs-dualsystem-desktop-presentation.md |
Plans dual-system rewind/run-ahead. |
deploy/Dockerfile.raproxy |
Updates the Python image. |
deploy/Dockerfile |
Updates Rust and Debian images. |
crates/rustynes-test-harness/tests/vs_system_rgb.rs |
Adopts fixed-size slice chunks. |
crates/rustynes-test-harness/tests/vs_dualsystem.rs |
Adopts fixed-size slice chunks. |
crates/rustynes-test-harness/tests/snapshots/external_coverage__mapper_045_GA23C_Famicom_Yarou_Vol_1_7_in_1_Unl.snap |
Updates the corrected framebuffer baseline. |
crates/rustynes-test-harness/tests/provenance_record_audit.rs |
Removes the shader-header exception. |
crates/rustynes-test-harness/tests/mister_source_map_audit.rs |
Updates empty-result assertions. |
crates/rustynes-test-harness/tests/holy_mapperel.rs |
Adopts fixed-size slice chunks. |
crates/rustynes-test-harness/tests/contribution_checklist_audit.rs |
Updates an empty-result assertion. |
crates/rustynes-test-harness/src/lib.rs |
Updates a version assertion. |
crates/rustynes-test-harness/src/coverage.rs |
Adopts fixed-size slice chunks. |
crates/rustynes-test-harness/src/bin/zapper_light_probe.rs |
Removes dead state and modernizes chunking. |
crates/rustynes-test-harness/src/bin/vs_dual_trace.rs |
Adopts fixed-size slice chunks. |
crates/rustynes-test-harness/src/bin/smb3_dma_trace.rs |
Adopts fixed-size slice chunks. |
crates/rustynes-test-harness/src/bin/fds_trace.rs |
Validates the complete FDS magic. |
crates/rustynes-test-harness/src/bin/fds_swap_repro.rs |
Adopts fixed-size slice chunks. |
crates/rustynes-test-harness/src/bin/fds_smoke.rs |
Adopts fixed-size slice chunks. |
crates/rustynes-test-harness/golden/tricnes/tricnes-harness-src/tricnes-harness.csproj |
Removes vendored TriCNES project source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-harness-src/mappers/Mapper_NROM.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-harness-src/mappers/Mapper_CNROM.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/Properties/Settings.Designer.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/Properties/AssemblyInfo.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/Program.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/packages.config |
Removes vendored TriCNES configuration. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/mappers/Mapper_NROM.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/mappers/Mapper_CNROM.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/LICENSE |
Removes the relocated source license copy. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/forms/TriCNTViewer.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/Forms/TriC72PinConnector.Designer.cs |
Removes vendored TriCNES source. |
crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/App.config |
Removes vendored TriCNES configuration. |
crates/rustynes-script/src/mlua_backend.rs |
Modernizes framebuffer chunking. |
crates/rustynes-script/src/lib.rs |
Updates empty-result assertions. |
crates/rustynes-script/Cargo.toml |
Inherits the workspace MSRV. |
crates/rustynes-probe/src/atlas.rs |
Modernizes WRAM chunking. |
crates/rustynes-ppu/src/state_trace.rs |
Modernizes record parsing. |
crates/rustynes-ppu/src/lib.rs |
Updates a version assertion. |
crates/rustynes-ppu/Cargo.toml |
Adds toolchain documentation. |
crates/rustynes-netplay/src/transport.rs |
Updates empty-poll assertions. |
crates/rustynes-netplay/src/signaling.rs |
Updates an empty-action assertion. |
crates/rustynes-netplay/src/relay.rs |
Modernizes hash-block parsing. |
crates/rustynes-mappers/tests/battery_sram_exposed.rs |
Makes header mutation const-capable. |
crates/rustynes-mappers/src/ntdec.rs |
Restores detailed provenance. |
crates/rustynes-mappers/src/lib.rs |
Updates a version assertion. |
crates/rustynes-mappers/src/kaiser.rs |
Restores detailed provenance. |
crates/rustynes-mappers/src/homebrew_boards.rs |
Updates an empty-save assertion. |
crates/rustynes-mappers/src/header.rs |
Simplifies test input construction. |
crates/rustynes-mappers/src/bmc_simple.rs |
Uses slice filling for bank tables. |
crates/rustynes-mappers/Cargo.toml |
Adds toolchain documentation. |
crates/rustynes-libretro/src/lib.rs |
Modernizes pixel conversion. |
crates/rustynes-libretro/src/abi_tests.rs |
Modernizes file and pixel checks. |
crates/rustynes-libretro/rustynes_libretro.info |
Bumps displayed core version. |
crates/rustynes-libretro/Cargo.toml |
Adds toolchain documentation. |
crates/rustynes-ios/src/audio_ring.rs |
Uses f32::midpoint. |
crates/rustynes-gfx-shaders/src/lib.rs |
Records Bisqwit shader provenance. |
crates/rustynes-gfx-shaders/src/crt_stack.rs |
Updates non-empty assertions. |
crates/rustynes-gamedb/Cargo.toml |
Adds toolchain documentation. |
crates/rustynes-frontend/web/Trunk.toml |
Pins Binaryen wasm-opt. |
crates/rustynes-frontend/src/wasm_io.rs |
Simplifies result checking. |
crates/rustynes-frontend/src/perf.rs |
Updates an empty-ring assertion. |
crates/rustynes-frontend/src/patch.rs |
Makes endian parsing const-capable. |
crates/rustynes-frontend/src/movie_srt.rs |
Updates empty-string assertions. |
crates/rustynes-frontend/src/input_macros.rs |
Updates an empty-bank assertion. |
crates/rustynes-frontend/src/icon.rs |
Modernizes RGB conversion. |
crates/rustynes-frontend/src/i18n.rs |
Updates non-empty assertions. |
crates/rustynes-frontend/src/help_tui.rs |
Makes topic lookup const-capable. |
crates/rustynes-frontend/src/genie_encode.rs |
Makes nibble encoding const-capable. |
crates/rustynes-frontend/src/genie_db.rs |
Updates empty-result assertions. |
crates/rustynes-frontend/src/emu_thread.rs |
Corrects tick-interval documentation. |
crates/rustynes-frontend/src/debugger/tastudio_panel.rs |
Updates an empty-request assertion. |
crates/rustynes-frontend/src/debugger/mod.rs |
Corrects detach UI documentation. |
crates/rustynes-frontend/src/debugger/memory_panel.rs |
Updates an empty-cheat assertion. |
crates/rustynes-frontend/src/debugger/memory_compare_panel.rs |
Updates an empty-cheat assertion. |
crates/rustynes-frontend/src/debugger/latency_panel.rs |
Updates an empty-status assertion. |
crates/rustynes-frontend/src/debugger/divergence_panel.rs |
Updates an empty-status assertion. |
crates/rustynes-frontend/src/debugger/cpu_panel.rs |
Updates empty-poke assertions. |
crates/rustynes-frontend/src/debugger/callstack.rs |
Removes redundant must_use. |
crates/rustynes-frontend/src/debugger/badge_cache.rs |
Modernizes PNG conversion. |
crates/rustynes-frontend/src/debugger/atlas_panel.rs |
Updates cleared-state assertions. |
crates/rustynes-frontend/src/debugger/access_counter.rs |
Simplifies mutable iteration. |
crates/rustynes-frontend/src/config.rs |
Modernizes palette parsing. |
crates/rustynes-frontend/src/cli.rs |
Updates a non-empty assertion. |
crates/rustynes-frontend/src/cheats.rs |
Updates empty-cheat assertions. |
crates/rustynes-frontend/src/audio.rs |
Uses f32::midpoint. |
crates/rustynes-frontend/src/about_fx.rs |
Modernizes PNG conversion. |
crates/rustynes-cpu/src/snapshot.rs |
Clarifies NMI snapshot state. |
crates/rustynes-cpu/src/lib.rs |
Updates a version assertion. |
crates/rustynes-cpu/Cargo.toml |
Adds toolchain documentation. |
crates/rustynes-cosim/Cargo.toml |
Bumps version and Rust requirement. |
crates/rustynes-core/src/nes.rs |
Updates empty and label assertions. |
crates/rustynes-core/src/movie.rs |
Modernizes rolling-hash chunking. |
crates/rustynes-core/src/movie_interop.rs |
Simplifies controller letters. |
crates/rustynes-core/src/lib.rs |
Updates a version assertion. |
crates/rustynes-core/src/hardware_options.rs |
Raises and documents the emulation epoch. |
crates/rustynes-core/src/cpu_boot_trace.rs |
Modernizes record parsing. |
crates/rustynes-core/src/bus.rs |
Retains compatible boxed-array iteration. |
crates/rustynes-core/Cargo.toml |
Adds toolchain documentation. |
crates/rustynes-core/benches/snapshot_restore.rs |
Clarifies benchmark math and chunking. |
crates/rustynes-cheevos/src/lib.rs |
Updates empty-list assertions. |
crates/rustynes-cheevos/Cargo.toml |
Inherits the workspace MSRV. |
crates/rustynes-apu/src/lib.rs |
Updates a version assertion. |
crates/rustynes-apu/src/blip.rs |
Updates an empty-buffer assertion. |
crates/rustynes-apu/Cargo.toml |
Adds toolchain documentation. |
CONTRIBUTING.md |
Updates toolchain and MSRV guidance. |
Cargo.toml |
Bumps the release and workspace MSRV. |
ARCHITECTURE.md |
Updates release and MSRV metadata. |
android/app/build.gradle.kts |
Bumps Android version and JSON library. |
.pre-commit-config.yaml |
Updates Ruff. |
.gitignore |
Removes obsolete TriCNES build exclusions. |
.github/workflows/web.yml |
Updates Python and optimizer documentation. |
.github/workflows/toolchain-canary.yml |
Updates toolchain history. |
.github/workflows/security.yml |
Updates install-action. |
.github/workflows/release.yml |
Moves releases to macOS 15. |
.github/workflows/pgo.yml |
Updates the toolchain reference. |
.github/workflows/ios.yml |
Updates the toolchain reference. |
.github/release-notes/v2.6.7.md |
Corrects a historical release claim. |
.github/actions/rust-setup/action.yml |
Updates the pinned setup action. |
.cargo/config.toml |
Updates resolver/MSRV documentation. |
Files not reviewed (10)
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/Forms/TriC72PinConnector.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/Properties/Resources.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/Properties/Settings.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/forms/TASProperties.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/forms/TASProperties3ct.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/forms/TriCHexEditor.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/forms/TriCNESGUI.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/forms/TriCNTViewer.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/forms/TriCTASTimeline.Designer.cs: Generated file
- crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/forms/TriCTraceLogger.Designer.cs: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
#590's first ready-mode run had two failures. Both came from this release's own changes, which no earlier run had exercised. 1. Android, NDK cross-build + UniFFI bindings: "Resolve NDK" exited 1 after `yes: standard output: Broken pipe`. v3.0.1 replaced the runner's preinstalled NDK with an `sdkmanager --install "ndk;30.0.16248370"` step under `set -euo pipefail`, and fed it the licence answers with `yes | sdkmanager`. Once sdkmanager finishes and closes the pipe, `yes` exits with EPIPE (or is killed by SIGPIPE: 141). Under pipefail that status fails the pipeline even when the install succeeded. sdkmanager's own output goes to /dev/null, so the log showed nothing else. Reproduced locally: `set -euo pipefail; yes | head -1` exits 141. Fix in all three Resolve NDK steps: `(yes || true) | sdkmanager ...`. Only yes's status is absorbed. Checked: - a succeeding installer gives rc 0; - `(yes || true) | false` still gives rc 1; - the existing `test -x .../clang` check still fails the step if the NDK is not on disk. Not run before: the heavy Android jobs only run on a ready PR, and this is the first ready run since the NDK move (4aae8c1). 2. Pages, build demo + docs: `mkdocs build --strict` aborted on two warnings. They came from docs/history/AGENTS-archive.md (de90569), whose verbatim AGENTS.md paragraphs keep repo-root links (`docs/adr/0043-...`, `docs/adr/0041-...`); from inside docs/history/ those links point nowhere. The archive is engineering-only material, the same class as `agents/`, `archive/` and `audits/`, which the published handbook already excludes. `history/` joins `exclude_docs`, so the archive stays verbatim and the rewriting hook has nothing to do. A local `mkdocs build --strict` with the CI's pinned mkdocs-material range now builds with no warning. The run also logs one INFO-level performance.md anchor message; it is older than this change and not a strict-mode failure. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
…t scores Records the v3.0.1 investigation of two Socket findings on #590 and main: GHSA-qwgh-2vcv-g2f7 against block-buffer 0.10.4 (locked only as an optional dependency of ratatui's unused termwiz backend; 0 compiled paths in every build; no upstream fix exists), and the supply-chain scores of tokio, libc and objc2 (fresh releases by their usual maintainers; libc's first Trusted Publishing release; tokio wasm-only). Commands and numbers are in the bullets. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
3175827 moved the libretro buildbot to Rust 1.99 and gave every crate the workspace floor. It left the split described as current in places the first sweep missed (`git grep 1.96`, not run then). Copilot (#590, #591, #593), CodeRabbit (#591) and agy (#590, #591, #593) each reported subsets. Fixed: - the seven libretro-path Cargo.toml files: the "builds this crate on Rust 1.96.0" comment is deleted, since the inherited workspace value is the fact; - .cargo/config.toml (the MSRV-aware resolver note); - ARCHITECTURE.md's tree line; - CONTRIBUTING.md's MSRV bullet; - docs/build-and-tooling.md; - docs/dev/BUILD.md, whose RUSTUP_TOOLCHAIN=1.96.0 command would now be rejected by cargo; - docs/dev/STYLE_GUIDE.md; - docs/benchmarks.md; - the webOS comment in .gitlab-ci.yml, plus a header on its #91899 post-mortem saying which two details have moved since (the pin value, and `rustup toolchain install ... --target` in place of `rustup target add`; Copilot on #592); - the bus.rs reborrow comment. The `&mut *self.ram` reborrow is KEPT: iterating `&mut Box<[T; N]>` needs 1.97+, both forms compile on the 1.99 floor, and changing working code buys nothing (agy suggested removing it). Kept: every 1.96 reference that is dated history (CHANGELOG entries, the 2026-07-20 buildbot post-mortem, rust-toolchain.toml's account of the split, the plans). A second `git grep -E "1\.96"` after this change shows only those. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
Bot findings from the v3.0.1 release PR and its CodeRabbit slices. Each code fix below was pinned by a test that failed before it; the mutation results are listed per item. - list_unresolved_threads.py (Copilot #590, #592). This is the bot-closeout merge gate, and three inputs could still reach its "0 unresolved thread(s)" all-clear: - a TRUNCATED list: the query asks for `first:100` and nothing checked `pageInfo`. The payload must now carry `reviewThreads.pageInfo.hasNextPage`, the script refuses if it is missing, and refuses if it is true. The README query selects it. - an open thread with an EMPTY comment list, which was skipped (only a partial query can produce one); - an open thread with no `id`, which crashed with a KeyError instead of a named error. Selftest +4 cases. They were red before (the first two, and these two printed FAIL). Mutation: disabling the more-pages refusal fails "a list with more pages is refused" (caught). - nes_golden_export.rs (Copilot #591). On an IRQ-trace overflow the CSV was already withheld, but an irq.csv and ckpt.bin from a PREVIOUS successful run with the same stem survived beside this run's obs.bin and read as its output. Both are now removed before the refusal (a missing file is fine; any other error is reported). The test pre-creates both stale files. It was red before ("an irq.csv ... was left on disk") and passes now: 8/8 bin tests. - release_anchor_audit.rs (Copilot #592). The release-title check refused `**`, `__`, backticks, `~~` and `](`, but not a PAIRED single delimiter: `(an *important* fix)` and `(an _important_ fix)` render literally in the plain-text GitHub release title. A new `has_emphasis_pair` refuses `*` or `_` that opens emphasis (not after a word character, not before space) and is later closed (not after space, not before a word character). `3*4`, `snake_case`, `2 * 3 * 4` and `major_version` stay allowed. Red before (left: None, right: Some("*")); 16/16 pass now, the real CHANGELOG header included. - Records: - NOTICE's TriCNES entry still said, in two places, that the source is in the tree (Copilot #593). - oracle-tooling-setup section 2a was titled "In-repo" and gave no destination for restoring the harness. It now gives a tested command that extracts straight to ~/reference-oracles (`git archive 416fe7d^:<path> | tar -x -C <dir>`), never into the working tree. - ARCHITECTURE.md's Last Updated date (in the previous commit). - "Artefact" -> "Artifact" in the hardware plan; the docs use "artifact" 302 times to 34. Declined or refuted, with the evidence in the replies: - agy's "ios/project.yml missing from MANIFESTS" (blocking): it is at bump_release.py:77. - agy's misplaced cosim doc comment: it is already on rn_write_observables. - agy's LICENSES count mismatch: 338 committed .nes and 33 sub-test ROMs, counted; the old 328 and 26 were stale on main. - agy's as_chunks stability doubt: stable since 1.88, and CI builds it. - The bump_release regex and gsub suggestions: the patterns are strict on purpose and fail closed. Gates: fmt; clippy on rustynes-test-harness and rustynes-cosim; both pr-review selftests; ruff; markdownlint. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
|
Answering the Antigravity review (both rounds):
|
Review round 2 on the v3.0.1 slices (CodeRabbit on #592). 1. libretro-cross holds the new check that .gitlab-ci.yml's RUSTUP_TOOLCHAIN equals rust-toolchain.toml's channel. But its path filter (`libretro`) did not list .gitlab-ci.yml, so a PR changing only that file -- exactly the change the check guards -- skipped the job and passed. It now lists .gitlab-ci.yml. Two inputs the libretro core compiles were also missing from the filter: - crates/rustynes-gamedb, one of the seven libretro-path crates; - vendor/, which holds the patched rust-libretro-sys. Both are added; actionlint passes. 2. list_unresolved_threads.py checked only one end of the page range. A payload fetched with `after:` can be the LAST page: it has `hasNextPage: false` while earlier pages hold open threads, and it still printed the all-clear. The gate now requires `hasPreviousPage` and refuses if it is missing or true; the README query selects it. Two new selftest cases were red before and pass now (13 checks in all). Declined on the same review: the request to delete the TriCNES trees and turn the README back to I/O-only. The trees are deleted in the release PR (#590, 416fe7d), not in this slice, so that half is a cross-slice finding. The consultation terms are the maintainer's decision D7 (2026-10-07), recorded in the guardrails' section 3a. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
|
Answering the Antigravity review (round 2):
|
… fixed All 14 inline findings held. The review body had no outside-diff, nitpick or duplicate section. Toolchain: - docs/build-and-tooling.md's Channel bullet still told readers the libretro buildbot runs 1.96.0, the floor is checked on every PR, and a pin bump is a one-line edit. Round 1 (eecf8ea) fixed the MSRV bullet one line above and missed this one. It now says the buildbot uses the same toolchain and that both files move together (CI fails otherwise), and gives the 1.96 exception as history. - docs/agents/libretro.md: the `-C ar` bullet is marked HISTORICAL, pointing to the "UNDID THE SPLIT" bullet for the current state. Measurements: - docs/scheduler.md and docs/performance.md called both frame costs "on the shipped fast dot path". Only nestest (~3.95 ms) is. flowing_palette (~2.65 ms) is rendering-disabled, so its `_fast` variant's guard bails and never enters that path; the bench source says so (full_frame.rs:125-126). It is now labelled as the control it is. Records: - CHANGELOG [3.0.1] Verification links docs/STATUS.md. - OVERVIEW: v2.0.0 is "the first designated breaking release", not "the one" (v3.0.0 is MAJOR too). - VERSION-PLAN's MAJOR rule: the new-deliverable trigger applies when that release is DESIGNATED MAJOR (ADR 0043 allows a minor; D1/D29 place it at the end of v3.9.x). - The v3.0.1 plan separates its reply count from its resolve count: 279 replies = 153 thread replies + 126 PR comments; 77 resolves. External facts, checked before editing: - ADR 0035 and the v3.9.0 plan, Android developer verification. It started 2026-09-30 in four countries for apps distributed through the participating stores, and goes to certified devices worldwide in 2027. ADB and the "advanced flow" still install an unregistered app. The account paths: an individual gives photo ID and proof of address; an organisation gives organisation documents; limited distribution covers up to 20 devices without a government ID. Which path to take stays an open maintainer decision. (developer.android.com developer-verification guide; Help Net Security.) - v3.9.0, iOS signing: iPhone and iPad uploads require the iOS/iPadOS 27 SDK from April 2027, as Apple announced on 2026-09-09; Xcode 27 is the tool that provides it. The plan previously called the date an inference. - v3.4.0, hosting: "well inside the free 1,000 GB" had no forecast behind it. It is replaced by the capacity it implies: about 45,000 relayed player-hours a month at the inferred 22 MB per player-hour, with the allowance shared with Realtime SFU. The gate now measures the first month's traffic. - v3.4.0, privacy: RetroAchievements' privacy-policy requirement and F-Droid's NonFreeNet anti-feature are now separate items. The anti-feature does not itself require a policy. Also: - v4.0.0, T-API-ENUMS now covers rustynes-netplay (NetMessage among its enums), which rustynes-core does not re-export. - The v2.0.x plan: the blank line splitting two blockquotes (MD028) is now `>`. Checks: markdownlint (pre-commit) on the linted files; the pinned 0.49.1 on copies of the new plans, which reports only two MD049 errors older than this change, in the ignored v2.0.x plan's lines 95-96; release_anchor 14/0, release_state_prose 16/0, contribution_checklist 9/0, each run on its own. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj
|
Answering the Antigravity review (round 3): both items were answered in round 2 and stand -- the |
|
Answering the Antigravity review (round 4): the |


v3.0.1 "Mortar"
A maintenance release on v3.0.0. Release notes:
.github/release-notes/v3.0.1.md; CHANGELOG[3.0.1]; planto-dos/plans/v3.0.1-mortar-plan.md.Breaking:
EMULATION_EPOCH1 -> 2 (the mapper 45 fix changes one game's output), so v3.0.0 movies and netplay peers are refused with both epochs named. Save states are unaffected.What changes
test/libretro-rust-1.99: pipeline 119614, 15/15 jobs, Apple included.libretro-crossnow fails if.gitlab-ci.ymlandrust-toolchain.tomldisagree. Every dependency, action, Android, web and Docker image moves to its newest release; this supersedes chore(ci): bump taiki-e/install-action from 2.87.21 to 2.87.22 #584.bump_release.py: the Cargo.toml anchor no longer matches internal X.0.0 requirements.MiSTer (sibling, private)
7e81a718..., off-die88d1dfa5....Verification (on the release tree)
Not done
The palette-offset A/B (8f449691 -> 33ea0572) is still waiting for a quiet host.
Review
CodeRabbit skips PRs over 100 files, so it reviews three stacked, review-only slices:
review/v3.0.1-a(crates),-b(harness, CI, scripts) and-c(docs, plans). They will be closed unmerged.The merge waits for the maintainer.
🤖 Generated with Claude Code
https://claude.ai/code/session_014qfTKi2M3swo7qnwvYCkDj