fix(accuracysnes): diagnose the Mesen2 oracle; flag an unverified figure - #287
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📜 Recent review details⏰ Context from checks skipped due to timeout. (4)
🧰 Additional context used📓 Path-based instructions (11)crates/**/*.rs📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
**/*.rs📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*.{rs,md}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
crates/rustysnes-*/**/*📄 CodeRabbit inference engine (Custom checks)
Files:
**/*.{rs,toml}📄 CodeRabbit inference engine (AGENTS.md)
Files:
crates/**⚙️ CodeRabbit configuration file
Files:
docs/**/*.md📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
docs/**/*📄 CodeRabbit inference engine (docs/testing-strategy.md)
Files:
docs/**⚙️ CodeRabbit configuration file
Files:
**/*.md⚙️ CodeRabbit configuration file
Files:
🧠 Learnings (2)📚 Learning: 2026-07-21T01:34:22.909ZApplied to files:
📚 Learning: 2026-07-21T05:22:58.848ZApplied to files:
🔇 Additional comments (3)
WalkthroughThe changes correct AccuracySNES timing-budget arithmetic, document instrumentation overhead, revise the A5.18 assessment, diagnose a pre-existing Mesen2 headless oracle hang, and increase ChangesAccuracySNES timing and runner alignment
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 10✅ Passed checks (10 passed)
Comment |
Added: the instrument-overhead attribution is unverifiedScoping the cheaper measurement instrument (the second The measurement is real; the attribution is not.
What is and is not in doubt. Every shipped result is a differential where the intercept cancels — What rests on the 189 figure is the The next step is one five-line test: |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/accuracysnes-plan.md`:
- Around line 571-616: Complete the C7.06 four-injection mapping by documenting
code 3 as the third assert_a8 call in c7_06, which checks phase B’s Range Over
result, alongside codes 1, 2, and 4 in docs/accuracysnes-plan.md lines 571-616.
Mirror the same corrected code-3 description in CHANGELOG.md lines 32-55 within
the “Four injections” sentence.
In `@scripts/accuracysnes/crossval.sh`:
- Around line 159-166: Update the cross-validation logic around
SNES9X_KNOWN_FAILURES so it verifies the actual failing test identities, not
just that the total count equals 14. Preserve the documented known-failure set
and reject runs where an expected failure is missing or an unrelated test
replaces it.
In `@scripts/accuracysnes/mesen_crossval.lua`:
- Around line 24-29: Revert the MAX_FRAMES increase in the Mesen2
cross-validation runner and prevent this step from running unattended in CI
while the documented hang remains unresolved. Update the CI gating or skip logic
associated with mesen_crossval.lua, preserving an explicit way to run it
manually for investigation.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 1aa65e77-6390-4cb6-9745-7908a2023eac
⛔ Files ignored due to path filters (6)
docs/accuracysnes-coverage.mdis excluded by!docs/accuracysnes-coverage.mdand included bydocs/**tests/roms/AccuracySNES/ERROR_CODES.mdis excluded by!tests/roms/AccuracySNES/ERROR_CODES.mdand included bytests/**tests/roms/AccuracySNES/SOURCE_CATALOG.tsvis excluded by!**/*.tsv,!tests/roms/AccuracySNES/SOURCE_CATALOG.tsvand included bytests/**tests/roms/AccuracySNES/asm/tests_group_a.sis excluded by!tests/roms/AccuracySNES/asm/tests_group_a.sand included bytests/**tests/roms/AccuracySNES/build/accuracysnes-pal.sfcis excluded by!tests/roms/AccuracySNES/build/**and included bytests/**tests/roms/AccuracySNES/build/accuracysnes.sfcis excluded by!tests/roms/AccuracySNES/build/**and included bytests/**
📒 Files selected for processing (7)
CHANGELOG.mdcrates/rustysnes-test-harness/tests/accuracysnes.rsdocs/accuracysnes-plan.mdscripts/accuracysnes/crossval.shscripts/accuracysnes/mesen_crossval.luatests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
- GitHub Check: test-light
- GitHub Check: lint
- GitHub Check: accuracysnes
- GitHub Check: build demo + docs
🧰 Additional context used
📓 Path-based instructions (16)
**/*
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*: Do not commit or vendor the generatedsnesdev_wiki/mirror; it is gitignored and intended only as a local reference.
Keep commits focused and use Conventional Commits:<type>(<scope>): <subject>, with an imperative subject of at most 72 characters.
Do not use emojis in code, comments, or commit messages.
Before opening a PR, ensure formatting, Clippy, workspace tests, the core embedded build, rustdoc with warnings denied, documentation coverage, and changelog requirements pass.
Ticket completion must be reflected in the relevantto-dos/sprint file.
**/*: Preserve the one-directional crate graph: chip crates must not depend on one another;rustysnes-coreties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keepdocs/STATUS.mdas the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNESv2.0orengine-lineageanchors as project releases.
Files:
scripts/accuracysnes/mesen_crossval.luascripts/accuracysnes/crossval.shcrates/rustysnes-test-harness/tests/accuracysnes.rstests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rsCHANGELOG.mddocs/accuracysnes-plan.md
scripts/accuracysnes/**
⚙️ CodeRabbit configuration file
scripts/accuracysnes/**: The cross-validation harness: the same AccuracySNES image is run on snes9x (through a
libretro host in C) and on Mesen2 (through its test runner and a Lua script), and their
verdicts are compared with the cart's. Its integrity is the whole argument for the
battery, so flag anything that could make a reference appear to agree — a verdict parsed
loosely, a missing-file path that degrades to success, a scene comparison that skips
rather than fails when the golden is absent. A known reference divergence belongs in
SNES9X_KNOWN_FAILURESwith a source citation, never in a widened match.
Files:
scripts/accuracysnes/mesen_crossval.luascripts/accuracysnes/crossval.sh
crates/**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
crates/**/*.rs: Preserve the master-clock lockstep timing model.
rustysnes-core::Busowns mutable machine state, and the CPU borrows&mut Bus.
Preserve determinism: seed, ROM, and input must produce bit-identical output.
Treat test ROMs as the behavioral specification; when documentation disagrees with passing ROM behavior, update the documentation.
Keepunsafeconfined to existing allowed areas, namely frontend and FFI code, and document everyunsafeblock with a// SAFETY:comment.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
**/*.rs
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*.rs: Use Rust edition 2024 and the toolchain pinned inrust-toolchain.toml(Rust 1.96).
Runcargo fmt --all --check; Rust code must remain rustfmt-compliant.
Run Clippy withcargo clippy --workspace --all-targets -- -D warnings; warnings must not remain.
New public Rust items must have rustdoc becausemissing_docsis a workspace lint.
Do not runcargo clippy --all-features;scriptingandscript-wasmare mutually exclusive. Use explicit per-feature jobs instead.
**/*.rs: Do not introduce.unwrap(),.expect(), orpanic!()on untrusted external input—such as ROM/save-state bytes, netplay messages, Lua or scripting input, or user-supplied paths—outside#[cfg(test)]code. Use typed errors at those boundaries; locally constructed values or values immediately protected by a checked invariant are allowed.
Every newunsafe { ... }block orunsafe fnmust have an adjacent// SAFETY:comment naming the relied-on invariant and its guarantor. Unsafe code outside the frontend and FFI shims should additionally be questioned becauseunsafe_codeis a workspace lint.
**/*.rs: Use Rust edition 2024 with the pinned 1.96 toolchain; satisfy workspacepedantic,nursery,missing_docs, andunsafe_codewarnings because CI runs with-D warnings. Document every public item.
Keepunsafecode restricted to the frontend and FFI, and include a// SAFETY:justification for each use.
Keep hot paths allocation-free.
Treatrustysnes_core::Busas the owner of mutable emulator state; the CPU borrows&mut Bus.
Use the master clock at 21477270 Hz as the timing master; advance the scheduler in lockstep and run other chips on their divisors.
Maintain determinism: seed, ROM, and input must produce bit-identical audio/video; frontend rate control must not alter emulation results.
When implementing hardware behavior, pin and run the failing test ROM first; treat test ROMs as the specification.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rstests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rs
**/*.{rs,md}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Chip-behavior changes must update both the chip implementation and the corresponding
docs/<subsystem>.mddocumentation.A chip change must update both the chip implementation and its corresponding
docs/<chip>.mddocumentation in the same change.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rstests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rsCHANGELOG.mddocs/accuracysnes-plan.md
crates/rustysnes-*/**/*
📄 CodeRabbit inference engine (Custom checks)
For the full pull request diff against its base branch, any observable behavior change under
crates/rustysnes-<chip>/must be accompanied by an edit to the matchingdocs/<chip>.md; a crate change passes without documentation only when it does not alter observable behavior, with the non-behavioral change stated explicitly.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
**/*.{rs,toml}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,toml}: Additive features must be default-off so shipped/native,no_std, and wasm builds remain byte-identical.
Never use or configure--all-features; validate opt-in feature combinations individually as required by the project recipe.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rstests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rs
crates/**
⚙️ CodeRabbit configuration file
crates/**: Emulator core. Hot paths are allocation-free;unsaferequires a// SAFETY:comment
naming the invariant. Any change to save-stated fields needs aFORMAT_VERSIONbump and a
docs/adr/0006bump-log entry. Behavior changes must update the matchingdocs/<chip>.md
in the same change.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
tests/roms/AccuracySNES/gen/src/**/*
📄 CodeRabbit inference engine (Custom checks)
For the full pull request diff against its base branch, when a test or scene is added or removed under
tests/roms/AccuracySNES/gen/src/, verify itsdossier.rs::MAPentry, all required regenerated artifacts, and matching count changes indocs/accuracysnes-plan.md. Artifact presence must be judged from the path-filter exclusion list, not from unreadable contents.
Files:
tests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rs
tests/roms/AccuracySNES/gen/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Rebuild AccuracySNES after any change to
gen/orasm/; never hand-edit generatedasm/tests_group_a.sorasm/scenes.s.
Files:
tests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rs
tests/roms/AccuracySNES/gen/src/**
⚙️ CodeRabbit configuration file
tests/roms/AccuracySNES/gen/src/**: This generates a hardware-accuracy test cartridge. Judge each test by whether it can
distinguish the behavior it names from the alternatives, not by whether it passes.Flag, specifically:
- Vacuity. An assertion whose expected value is also what a broken or absent
implementation produces (zero, "unchanged", "not $FF") needs a paired control assertion
that would fail on that implementation. Say which alternative goes uncaught.- Overstated doc comments. The prose above a test is a claim about what it validates.
If it names behaviors the emitted program does not exercise, or asserts a rationale that
is not true of the code, that is a defect even though the test passes.- Shared state. OAM, CGRAM, VRAM and the S-DSP registers are not reset between tests. A
test that does not establish its own starting conditions may be measuring the previous
one; look for an earlier test that leaves the relevant state dirty.- Timing-marginal reads. Reading a register a few cycles after disturbing it, or
asserting on a value that is still moving, produces a verdict that flips when unrelated
code shifts. Prefer a settle, a disarm, or a provably stationary value.- Scanline geometry. Line 0 is a blanking line; the V counter's low byte aliases on a
312-line PAL frame; the visible height is 224 or 239 depending on overscan. Constants
derived from any of these deserve a second look.- Duplicate coverage.
dossier.rs::MAPmust not claim an assertion another test already
implements. There is a build gate for this, but flag it in review too.
Files:
tests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rs
CHANGELOG.md
📄 CodeRabbit inference engine (CONTRIBUTING.md)
User-visible changes must be recorded under the
[Unreleased]section.For the full pull request diff against its base branch, modify
CHANGELOG.mdwhen user-visible behavior changes, including emulator output, frontend features, CLI flags, public APIs, or AccuracySNES cartridge contents. Do not require it for purely internal changes, tests, comments, or CI configuration.
Files:
CHANGELOG.md
**/*.md
⚙️ CodeRabbit configuration file
**/*.md: Docs are the spec, not a changelog. Flag prose that has drifted from the code it describes
rather than style nits. The markdownlint gate is pinned to v0.39.0 via pre-commit —
do not report rules that version does not have (MD060 in particular).
Files:
CHANGELOG.mddocs/accuracysnes-plan.md
docs/**/*.md
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Before changing a subsystem, consult
docs/architecture.md,docs/STATUS.md,CONTRIBUTING.md, the relevant subsystem documentation, and applicable ADRs.New subsystems must add documentation under
docs/.
Files:
docs/accuracysnes-plan.md
docs/**/*
📄 CodeRabbit inference engine (docs/testing-strategy.md)
Chip crates should exceed 90% unit-test coverage, and each chip should be fuzzable in isolation.
Files:
docs/accuracysnes-plan.md
docs/**
⚙️ CodeRabbit configuration file
docs/**: Docs are the spec, not a history log. Flag claims that contradict the code, counts that
contradict the generateddocs/accuracysnes-coverage.md, and any statement of coverage that
is broader than what the corresponding test actually asserts.
Files:
docs/accuracysnes-plan.md
🧠 Learnings (5)
📚 Learning: 2026-07-21T02:10:49.581Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 190
File: tests/roms/AccuracySNES/gen/src/tests/ppu.rs:0-0
Timestamp: 2026-07-21T02:10:49.581Z
Learning: For any AccuracySNES tests that perform a runtime measurement investigation using writes to $2137 and $4201, do not reuse measurement/slot indices that may already be owned by another test. Before using a slot, verify it is unused (e.g., via an on-cart probe/readback that confirms the slot contains no prior test result). Then independently record $213F immediately before and immediately after each $2137/$4201 operation, so the test can attribute changes to its own operation and avoid cross-test interference.
Applied to files:
tests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/ppu.rs
📚 Learning: 2026-07-21T06:21:34.629Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 198
File: docs/accuracysnes-plan.md:0-0
Timestamp: 2026-07-21T06:21:34.629Z
Learning: When reviewing slot allocation for SNES/ROM measurement channels in generator-driven Rust code, account for slots assigned via generation-time computed writers (e.g., slots derived from formulas like `slot_base = 8 + index * 2`) rather than only literal `record(...)` calls. Trace the computed writer’s full emitted slot range and verify there are no collisions with other opcodes/channels that may use different slot ranges after earlier conflict resolution.
Applied to files:
tests/roms/AccuracySNES/gen/src/tests/ppu.rs
📚 Learning: 2026-07-22T06:12:00.703Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 201
File: tests/roms/AccuracySNES/gen/src/tests/apu.rs:1508-1538
Timestamp: 2026-07-22T06:12:00.703Z
Learning: When working on APU/voice timing in these AccuracySNES test sources, treat NTSC vs PAL differences as a first-class constraint. In particular, do not change `Voice::settle` (or any settling/code-size/timing logic it affects) unless you cross-validate against both regions (NTSC and PAL), because timing/polling phase shifts can cause regressions that may appear as PAL-only test failures. For ROM-specific expectations like `E7.13`, keep the intentionally chosen ENVX range (e.g., `0x68..=0x7C`) unless you re-validate that the absorbed post-KON timing variation still matches across both regions.
Applied to files:
tests/roms/AccuracySNES/gen/src/tests/ppu.rs
📚 Learning: 2026-07-21T01:34:22.909Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 189
File: docs/accuracysnes-plan.md:0-0
Timestamp: 2026-07-21T01:34:22.909Z
Learning: When reviewing the AccuracySNES documentation in docs/accuracysnes-*.md (notably docs/accuracysnes-plan.md vs the generated docs/accuracysnes-coverage.md), treat the reported metrics as intentionally non-equivalent: the battery test count and dossier assertion coverage are not interchangeable. Do not infer one count/coverage from the other during review (e.g., one test may contain multiple assertions, and multiple tests may contribute to a single assertion/row such as E6.02).
Applied to files:
docs/accuracysnes-plan.md
📚 Learning: 2026-07-21T05:22:58.848Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 197
File: docs/accuracysnes-plan.md:598-600
Timestamp: 2026-07-21T05:22:58.848Z
Learning: In the AccuracySNES documentation under `docs/`, when an assertion exists in the research dossier but cannot be measured/verified by the current cartridge timing test, distinguish the dossier assertion from test measurability: keep the original hardware assertion (and any contribution to the coverage denominator) intact, withdraw/stop using the specific test coverage only if the sources cannot decompose the required CPU-cycle timing into bus vs internal components, and mark the row as not measurable using the `[NOT CART-MEASURABLE ...]` annotation with links to the corresponding plan section (e.g., `docs/accuracysnes-plan.md` §A5.20) and the related roadmap/ticket (e.g., `to-dos/ROADMAP.md` ticket `T-06-A`). Ensure the documentation/coverage reporting treats the row as uncovered rather than removing or redefining the assertion.
Applied to files:
docs/accuracysnes-plan.md
🔇 Additional comments (5)
tests/roms/AccuracySNES/gen/src/dossier.rs (1)
215-215: LGTM!tests/roms/AccuracySNES/gen/src/tests/ppu.rs (1)
74-74: LGTM!Also applies to: 2444-2562, 2564-2607, 2609-2637
docs/accuracysnes-plan.md (1)
458-485: LGTM!Also applies to: 500-500
crates/rustysnes-test-harness/tests/accuracysnes.rs (1)
59-59: LGTM!CHANGELOG.md (1)
14-31: LGTM!
| # snes9x, +1 test (C7.06 "TimeOver by YLOC+1", fails phase A with code 1): the same scanline-granular | ||
| # sprite model as C7.05, seen from the other side. The row requires Time Over to be clear while the | ||
| # sprites' OWN line is being evaluated (V = 100) and set once the line they paint on has begun | ||
| # (V = 101 = YLOC + 1); snes9x already reads it set on V = 100, i.e. it flags the tile-budget | ||
| # overflow a line early because it evaluates and paints in the same pass. The 20-sprite/8x8 control | ||
| # passes there, so this is the position being wrong, not the budget. Documented by nocash fullsnes | ||
| # and the SNESdev Wiki (34-tile budget, raised by the fetch phase). Region-independent. | ||
| SNES9X_KNOWN_FAILURES=14 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect how SNES9X_KNOWN_FAILURES is used in crossval.sh.
fd 'crossval.sh' scripts/accuracysnes --exec grep -n -C 5 'SNES9X_KNOWN_FAILURES' {}Repository: doublegate/RustySNES
Length of output: 2140
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- crossval.sh relevant control flow ---'
sed -n '145,235p' scripts/accuracysnes/crossval.sh
printf '%s\n' '--- libretro_crossval.c structure and exit paths ---'
ast-grep outline scripts/accuracysnes/libretro_crossval.c
rg -n -C 6 'return|exit|fail|FAIL|argc|argv' scripts/accuracysnes/libretro_crossval.cRepository: doublegate/RustySNES
Length of output: 17831
Compare failing test identities, not only the failure count. A 15th failure returns exit code 15 and fails the check, but a run that replaces one known failure with an unrelated failure still returns 14 and passes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/accuracysnes/crossval.sh` around lines 159 - 166, Update the
cross-validation logic around SNES9X_KNOWN_FAILURES so it verifies the actual
failing test identities, not just that the total count equals 14. Preserve the
documented known-failure set and reject runs where an expected failure is
missing or an unrelated test replaces it.
Source: Path instructions
| -- Bounds the same run as the in-repo harness's MAX_FRAMES (1500), mesen_scenes.lua's (4000) and | ||
| -- libretro_crossval.c's max_frames (2000). This one was left at 900 when the others grew -- the | ||
| -- harness comment naming the budgets that must move together does not list this file, which is how | ||
| -- it was missed. It is NOT why this runner times out (4000 fails identically); see | ||
| -- docs/accuracysnes-plan.md on the Mesen2 oracle. | ||
| local MAX_FRAMES = 4000 |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔵 Trivial
Raising MAX_FRAMES to 4000 quadruples the wait time for a run already known to hang for the full duration.
docs/accuracysnes-plan.md documents that Mesen2 still times out at 4000 frames. Until the underlying hang is fixed, every invocation of this runner now waits over 4x longer than before (900 → 4000 frames) before failing, with no additional diagnostic value from the extra frames.
Consider gating or skipping this Mesen2 cross-validation step in CI until the root cause is resolved, rather than only raising the budget it hangs at. If it already runs unattended in CI, this is worth confirming so the wall-clock cost is intentional.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/accuracysnes/mesen_crossval.lua` around lines 24 - 29, Revert the
MAX_FRAMES increase in the Mesen2 cross-validation runner and prevent this step
from running unattended in CI while the documented hang remains unresolved.
Update the CI gating or skip logic associated with mesen_crossval.lua,
preserving an explicit way to run it manually for investigation.
Source: Path instructions
…dget Three v1.28.0 items ended at "no oracle can arbitrate", so the headless runner was investigated rather than accepted as environmental. Established: - The hang is genuinely pre-existing. The v1.25.0-era cartridge image, restored from git and run unmodified, times out identically. - It is NOT the frame budget. The battery needs ~431 frames on RustySNES and snes9x is given 2000; Mesen2 still times out at 4000. - The Lua never observes DONE = 0xA5, and the results magic never reads as ACSN -- under snesWorkRam with a $F000 offset it reads $A1, under snesMemory with the full $7EF000 address it reads $52; neither is the expected $41. So the failure is in the Lua-to-emulator memory bridge, or the battery not executing, and not a timeout. Exit code 254 has been misreporting the cause all along. - emu.log does not surface in --testrunner mode, so diagnosis has to be smuggled out through the process exit code. Not fixed. The plan records the next step -- exit with a byte the runtime writes early and whose value is known, then walk the address until it reads back -- and says not to spend further effort on rows needing an arbiter until this resolves. Separately real: mesen_crossval.lua's MAX_FRAMES was 900 against the harness's 1500, mesen_scenes.lua's 4000 and libretro_crossval.c's 2000. The harness comment naming the budgets that must move together omits this file, which is how it was missed. Aligned, and the comment corrected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The intercept is real -- A5.19's native arm reads 241 dots at 4 RTIs and 293 at 8, so 13 dots/RTI marginal and an intercept of 189. What is NOT established is that those dots are the instrument. hv_begin does sync to line start, but it stores THAT poll's own reading as the start marker, so the sync sits before the latch rather than inside the delta. What is genuinely inside is hv_begin's tail plus hv_end's head -- on the order of 30 dots. So ~160 are unaccounted for and nobody has measured the empty span. Every shipped result is a differential where the intercept cancels, so none is in doubt. What rests on the 189 figure is the A5.18 parking decision: 341 - 189 leaves ~152 usable and only 3 round trips fit. If the true overhead is nearer 30 the budget is ~310 and 8 fit, which would make A5.18 viable as first designed and its parking wrong. Next step is one five-line test: measure_begin immediately followed by measure_end, recorded to a slot. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fd92a0f to
f04f6bf
Compare
Review adjudicationNo blocking issues. One applied, one declined with the history, one moot after a rebase. Applied: the titleShortened to 72 characters (it was 73) and reworded to name the second commit, which flags the The other half of that finding — "omits mentioning the addition of test Declined: "all three budgets must move together"The harness comment says to check the other budgets in the same change, not to equalize them —
Worth restating from the PR body, since the finding reads as though the budget were the fix: it is Moot: the duplicate
|
…stored The empty span (measure_begin immediately followed by measure_end) is 175 dots, with 8 NOPs at exactly 28 dots marginal. So the instrument's marginal accuracy is exact, only its fixed cost is large, and A5.19's 189-dot intercept IS the instrument -- the v1.28.0 assessment was right the first time. The preceding commit called that figure into doubt on the strength of reading hv_begin/hv_end and estimating '~30 dots should be inside the delta'. That estimate was bad arithmetic: the pair runs ~70-90 CPU cycles between the latches and a cycle is 6-8 master clocks, 1.5-2 dots each. Counting instructions instead of cycles produced the discrepancy. Corrected budget: 341 - 175 = 166 dots, against the 152 the A5.18 note assumed. That moves a BRK round trip from 3 iterations to 4, giving 8 dots of signal against TOL 2 -- better than the 6 that parked it, still a difference-of-differences and still short of A5.19's 16. A5.18 stays parked on corrected arithmetic; the verdict is borderline, not impossible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Ran the empty-span test — my flag was wrongThe measurement I asked for in the previous commit: The instrument's fixed cost is 175 dots. So The control matters too: 8 Where the doubt came from. I read Consequence for The probe itself is not in this PR — it was temporary, and the tree is back to reproducing the |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
scripts/accuracysnes/mesen_crossval.lua (1)
29-29: 🧹 Nitpick | 🔵 TrivialGate the known Mesen2 hang before increasing unattended wait time.
MAX_FRAMES = 4000increases the maximum wait from 900 to 4000 frames while the plan documents that the runner still hangs at 4000. If CI invokes this script, each failure now waits for the longer limit before returning exit code 254. Confirm the invocation path and gate this diagnostic runner in unattended jobs while preserving manual execution.Verification command
#!/bin/bash set -euo pipefail rg -n -C 4 'mesen_crossval\.lua|MAX_FRAMES|testrunner' .🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/accuracysnes/mesen_crossval.lua` at line 29, Inspect the invocation paths for scripts/accuracysnes/mesen_crossval.lua and prevent unattended or CI jobs from running this diagnostic runner, while retaining its existing manual execution path. Update the relevant runner configuration or guard around the invocation rather than increasing MAX_FRAMES, and preserve the existing exit behavior for explicitly requested manual runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/accuracysnes-plan.md`:
- Around line 474-479: Update the remaining A5.18 references to consistently use
the corrected 166-dot budget: replace the obsolete 189-dot overhead, 152 usable
dots, three iterations, and six-dot signal in the surrounding assessment, and
revise or remove the outdated rationale near the final A5.18 conclusion. Ensure
docs/accuracysnes-plan.md presents only the corrected four-iteration, eight-dot
assessment.
- Line 481: Remove the duplicated Mesen2 heading text in the section heading,
leaving a single correctly formatted “The Mesen2 oracle — diagnosed, not yet
fixed (`v1.29.0`)” title.
In `@scripts/accuracysnes/mesen_crossval.lua`:
- Around line 24-28: Update the descriptions in
scripts/accuracysnes/mesen_crossval.lua (lines 24-28), docs/accuracysnes-plan.md
(lines 487-494), and CHANGELOG.md (lines 34-37) to distinguish the explicit
2000-frame invocation budget passed by scripts/accuracysnes/crossval.sh from
libretro_crossval.c’s 2400-frame default when no argument is provided; preserve
the existing budget relationships and do not describe 2000 as the default.
---
Duplicate comments:
In `@scripts/accuracysnes/mesen_crossval.lua`:
- Line 29: Inspect the invocation paths for
scripts/accuracysnes/mesen_crossval.lua and prevent unattended or CI jobs from
running this diagnostic runner, while retaining its existing manual execution
path. Update the relevant runner configuration or guard around the invocation
rather than increasing MAX_FRAMES, and preserve the existing exit behavior for
explicitly requested manual runs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 05e4ae0a-3431-46c4-a7cb-2c9422e9cc66
📒 Files selected for processing (4)
CHANGELOG.mdcrates/rustysnes-test-harness/tests/accuracysnes.rsdocs/accuracysnes-plan.mdscripts/accuracysnes/mesen_crossval.lua
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
- GitHub Check: lint
- GitHub Check: accuracysnes
- GitHub Check: build demo + docs
🧰 Additional context used
📓 Path-based instructions (13)
**/*
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*: Do not commit or vendor the generatedsnesdev_wiki/mirror; it is gitignored and intended only as a local reference.
Keep commits focused and use Conventional Commits:<type>(<scope>): <subject>, with an imperative subject of at most 72 characters.
Do not use emojis in code, comments, or commit messages.
Before opening a PR, ensure formatting, Clippy, workspace tests, the core embedded build, rustdoc with warnings denied, documentation coverage, and changelog requirements pass.
Ticket completion must be reflected in the relevantto-dos/sprint file.
**/*: Preserve the one-directional crate graph: chip crates must not depend on one another;rustysnes-coreties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keepdocs/STATUS.mdas the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNESv2.0orengine-lineageanchors as project releases.
Files:
scripts/accuracysnes/mesen_crossval.luacrates/rustysnes-test-harness/tests/accuracysnes.rsdocs/accuracysnes-plan.mdCHANGELOG.md
scripts/accuracysnes/**
⚙️ CodeRabbit configuration file
scripts/accuracysnes/**: The cross-validation harness: the same AccuracySNES image is run on snes9x (through a
libretro host in C) and on Mesen2 (through its test runner and a Lua script), and their
verdicts are compared with the cart's. Its integrity is the whole argument for the
battery, so flag anything that could make a reference appear to agree — a verdict parsed
loosely, a missing-file path that degrades to success, a scene comparison that skips
rather than fails when the golden is absent. A known reference divergence belongs in
SNES9X_KNOWN_FAILURESwith a source citation, never in a widened match.
Files:
scripts/accuracysnes/mesen_crossval.lua
crates/**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
crates/**/*.rs: Preserve the master-clock lockstep timing model.
rustysnes-core::Busowns mutable machine state, and the CPU borrows&mut Bus.
Preserve determinism: seed, ROM, and input must produce bit-identical output.
Treat test ROMs as the behavioral specification; when documentation disagrees with passing ROM behavior, update the documentation.
Keepunsafeconfined to existing allowed areas, namely frontend and FFI code, and document everyunsafeblock with a// SAFETY:comment.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
**/*.rs
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*.rs: Use Rust edition 2024 and the toolchain pinned inrust-toolchain.toml(Rust 1.96).
Runcargo fmt --all --check; Rust code must remain rustfmt-compliant.
Run Clippy withcargo clippy --workspace --all-targets -- -D warnings; warnings must not remain.
New public Rust items must have rustdoc becausemissing_docsis a workspace lint.
Do not runcargo clippy --all-features;scriptingandscript-wasmare mutually exclusive. Use explicit per-feature jobs instead.
**/*.rs: Do not introduce.unwrap(),.expect(), orpanic!()on untrusted external input—such as ROM/save-state bytes, netplay messages, Lua or scripting input, or user-supplied paths—outside#[cfg(test)]code. Use typed errors at those boundaries; locally constructed values or values immediately protected by a checked invariant are allowed.
Every newunsafe { ... }block orunsafe fnmust have an adjacent// SAFETY:comment naming the relied-on invariant and its guarantor. Unsafe code outside the frontend and FFI shims should additionally be questioned becauseunsafe_codeis a workspace lint.
**/*.rs: Use Rust edition 2024 with the pinned 1.96 toolchain; satisfy workspacepedantic,nursery,missing_docs, andunsafe_codewarnings because CI runs with-D warnings. Document every public item.
Keepunsafecode restricted to the frontend and FFI, and include a// SAFETY:justification for each use.
Keep hot paths allocation-free.
Treatrustysnes_core::Busas the owner of mutable emulator state; the CPU borrows&mut Bus.
Use the master clock at 21477270 Hz as the timing master; advance the scheduler in lockstep and run other chips on their divisors.
Maintain determinism: seed, ROM, and input must produce bit-identical audio/video; frontend rate control must not alter emulation results.
When implementing hardware behavior, pin and run the failing test ROM first; treat test ROMs as the specification.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
**/*.{rs,md}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Chip-behavior changes must update both the chip implementation and the corresponding
docs/<subsystem>.mddocumentation.A chip change must update both the chip implementation and its corresponding
docs/<chip>.mddocumentation in the same change.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rsdocs/accuracysnes-plan.mdCHANGELOG.md
crates/rustysnes-*/**/*
📄 CodeRabbit inference engine (Custom checks)
For the full pull request diff against its base branch, any observable behavior change under
crates/rustysnes-<chip>/must be accompanied by an edit to the matchingdocs/<chip>.md; a crate change passes without documentation only when it does not alter observable behavior, with the non-behavioral change stated explicitly.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
**/*.{rs,toml}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,toml}: Additive features must be default-off so shipped/native,no_std, and wasm builds remain byte-identical.
Never use or configure--all-features; validate opt-in feature combinations individually as required by the project recipe.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
crates/**
⚙️ CodeRabbit configuration file
crates/**: Emulator core. Hot paths are allocation-free;unsaferequires a// SAFETY:comment
naming the invariant. Any change to save-stated fields needs aFORMAT_VERSIONbump and a
docs/adr/0006bump-log entry. Behavior changes must update the matchingdocs/<chip>.md
in the same change.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
docs/**/*.md
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Before changing a subsystem, consult
docs/architecture.md,docs/STATUS.md,CONTRIBUTING.md, the relevant subsystem documentation, and applicable ADRs.New subsystems must add documentation under
docs/.
Files:
docs/accuracysnes-plan.md
docs/**/*
📄 CodeRabbit inference engine (docs/testing-strategy.md)
Chip crates should exceed 90% unit-test coverage, and each chip should be fuzzable in isolation.
Files:
docs/accuracysnes-plan.md
docs/**
⚙️ CodeRabbit configuration file
docs/**: Docs are the spec, not a history log. Flag claims that contradict the code, counts that
contradict the generateddocs/accuracysnes-coverage.md, and any statement of coverage that
is broader than what the corresponding test actually asserts.
Files:
docs/accuracysnes-plan.md
**/*.md
⚙️ CodeRabbit configuration file
**/*.md: Docs are the spec, not a changelog. Flag prose that has drifted from the code it describes
rather than style nits. The markdownlint gate is pinned to v0.39.0 via pre-commit —
do not report rules that version does not have (MD060 in particular).
Files:
docs/accuracysnes-plan.mdCHANGELOG.md
CHANGELOG.md
📄 CodeRabbit inference engine (CONTRIBUTING.md)
User-visible changes must be recorded under the
[Unreleased]section.For the full pull request diff against its base branch, modify
CHANGELOG.mdwhen user-visible behavior changes, including emulator output, frontend features, CLI flags, public APIs, or AccuracySNES cartridge contents. Do not require it for purely internal changes, tests, comments, or CI configuration.
Files:
CHANGELOG.md
🧠 Learnings (2)
📚 Learning: 2026-07-21T01:34:22.909Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 189
File: docs/accuracysnes-plan.md:0-0
Timestamp: 2026-07-21T01:34:22.909Z
Learning: When reviewing the AccuracySNES documentation in docs/accuracysnes-*.md (notably docs/accuracysnes-plan.md vs the generated docs/accuracysnes-coverage.md), treat the reported metrics as intentionally non-equivalent: the battery test count and dossier assertion coverage are not interchangeable. Do not infer one count/coverage from the other during review (e.g., one test may contain multiple assertions, and multiple tests may contribute to a single assertion/row such as E6.02).
Applied to files:
docs/accuracysnes-plan.md
📚 Learning: 2026-07-21T05:22:58.848Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 197
File: docs/accuracysnes-plan.md:598-600
Timestamp: 2026-07-21T05:22:58.848Z
Learning: In the AccuracySNES documentation under `docs/`, when an assertion exists in the research dossier but cannot be measured/verified by the current cartridge timing test, distinguish the dossier assertion from test measurability: keep the original hardware assertion (and any contribution to the coverage denominator) intact, withdraw/stop using the specific test coverage only if the sources cannot decompose the required CPU-cycle timing into bus vs internal components, and mark the row as not measurable using the `[NOT CART-MEASURABLE ...]` annotation with links to the corresponding plan section (e.g., `docs/accuracysnes-plan.md` §A5.20) and the related roadmap/ticket (e.g., `to-dos/ROADMAP.md` ticket `T-06-A`). Ensure the documentation/coverage reporting treats the row as uncovered rather than removing or redefining the assertion.
Applied to files:
docs/accuracysnes-plan.md
🔇 Additional comments (4)
docs/accuracysnes-plan.md (2)
458-466: LGTM!
594-594: LGTM!crates/rustysnes-test-harness/tests/accuracysnes.rs (1)
59-59: LGTM!CHANGELOG.md (1)
14-32: LGTM!
| **The corrected span budget is `341 - 175 = 166` dots**, slightly more generous than the 152 the | ||
| `A5.18` note assumed. That moves a `BRK` round trip (39 dots) from 3 iterations to **4**, giving 8 | ||
| dots of signal against `TOL` 2 — better than the 6 that parked it, still a difference-of-differences | ||
| accumulating quantisation from four measurements, and still well short of `A5.19`'s 16. `A5.18` | ||
| stays parked, on corrected arithmetic rather than a wrong figure; the honest verdict is *borderline*, | ||
| not *impossible*. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Update the remaining A5.18 arithmetic.
This section changes the budget to 166 dots and supports four iterations. The same plan still states:
- Line 513-515: approximately 189 dots of overhead and 152 usable dots.
- Line 520: only three iterations and six dots of signal.
- Line 541-544: the same obsolete rationale.
Replace those values with the corrected assessment, or remove the obsolete explanation. Otherwise, the plan gives two incompatible A5.18 designs.
As per path instructions, docs are the spec, not a history log.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/accuracysnes-plan.md` around lines 474 - 479, Update the remaining A5.18
references to consistently use the corrected 166-dot budget: replace the
obsolete 189-dot overhead, 152 usable dots, three iterations, and six-dot signal
in the surrounding assessment, and revise or remove the outdated rationale near
the final A5.18 conclusion. Ensure docs/accuracysnes-plan.md presents only the
corrected four-iteration, eight-dot assessment.
Source: Path instructions
| -- Bounds the same run as the in-repo harness's MAX_FRAMES (1500), mesen_scenes.lua's (4000) and | ||
| -- libretro_crossval.c's max_frames (2000). This one was left at 900 when the others grew -- the | ||
| -- harness comment naming the budgets that must move together does not list this file, which is how | ||
| -- it was missed. It is NOT why this runner times out (4000 fails identically); see | ||
| -- docs/accuracysnes-plan.md on the Mesen2 oracle. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 5 'libretro_crossval\.c|libretro_crossval|max_frames|MAX_FRAMES|mesen_crossval\.lua' .Repository: doublegate/RustySNES
Length of output: 50378
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- mesen_crossval.lua ---'
sed -n '20,32p' scripts/accuracysnes/mesen_crossval.lua
printf '%s\n' '--- accuracysnes-plan.md ---'
sed -n '487,495p' docs/accuracysnes-plan.md
printf '%s\n' '--- CHANGELOG.md ---'
sed -n '32,39p' CHANGELOG.md
printf '%s\n' '--- crossval.sh ---'
sed -n '168,176p' scripts/accuracysnes/crossval.sh
printf '%s\n' '--- libretro_crossval.c ---'
sed -n '217,241p' scripts/accuracysnes/libretro_crossval.cRepository: doublegate/RustySNES
Length of output: 3446
Describe 2000 as the explicit invocation budget.
scripts/accuracysnes/crossval.sh passes 2000; libretro_crossval.c defaults to 2400 when no argument is provided. Update the mesen_crossval.lua and CHANGELOG.md descriptions to distinguish these values.
📍 Affects 3 files
scripts/accuracysnes/mesen_crossval.lua#L24-L28(this comment)docs/accuracysnes-plan.md#L487-L494CHANGELOG.md#L34-L37
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/accuracysnes/mesen_crossval.lua` around lines 24 - 28, Update the
descriptions in scripts/accuracysnes/mesen_crossval.lua (lines 24-28),
docs/accuracysnes-plan.md (lines 487-494), and CHANGELOG.md (lines 34-37) to
distinguish the explicit 2000-frame invocation budget passed by
scripts/accuracysnes/crossval.sh from libretro_crossval.c’s 2400-frame default
when no argument is provided; preserve the existing budget relationships and do
not describe 2000 as the default.
The heading dedup is a real defect in the preceding commit -- the edit concatenated the section header onto itself. Raised in review. Also rephrased the frame-budget comment as the review suggested, and corrected 'all three' to 'all four': there are four budgets bounding the same run, and the comment previously named three because it omitted mesen_crossval.lua -- which is how that one drifted to 900 in the first place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Second adjudicationThe suggestion was a real defect of mine. The blocking finding is not reproduced. Applied: the duplicated headingReal, and my fault — the edit that replaced the instrument section concatenated the following Applied: the frame-budget rephrasingTaken, and one thing corrected beyond it: the comment said "all three bound the same run" when Not reproduced: "the CHANGELOG notes a
|
Antigravity review (Gemini via Ultra)This PR updates documentation and diagnostic notes regarding the Mesen2 test oracle and bumps Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by |
First
v1.29.0item. Threev1.28.0rows ended at "no oracle can arbitrate" —C7.07's errata,the scene goldens, and the dot half of
C7.05— so the Mesen2 headless runner was investigatedrather than accepted as "environmental", which is how it had been recorded.
What is now established
The hang is genuinely pre-existing. The
v1.25.0-era cartridge image, restored from git andrun unmodified, times out identically. Nothing in this session's rows caused it.
It is not the frame budget. The battery needs ~431 frames on RustySNES (
MAX_FRAMES1500),snes9x is given 2000, and Mesen2 still times out at 4000.
The Lua never observes
DONE = 0xA5, and the results magic never reads asACSN:emu.memType.snesWorkRam, offset$F000$A1$41('A')emu.memType.snesMemory, address$7EF000$52$41('A')So the failure is in the Lua-to-emulator memory bridge, or the battery not executing — not a
timeout. Exit code 254 has been misreporting the cause the entire time, which is why this looked
environmental.
emu.logdoes not surface in--testrunnermode. Both readings above had to be smuggled outthrough the process exit code (
emu.stop(value)); that is the only diagnostic channel availableand it is now written down.
Not fixed
I am not claiming a working oracle.
docs/accuracysnes-plan.mdrecords the next step — exit with abyte the runtime writes early and whose value is known, then walk the address until it reads back;
if the bridge is at fault the fix is a one-line
memType/offset change, and if the battery genuinelydoes not execute that is a much larger Mesen2 configuration question. It also says plainly not to
spend further effort on rows that need an arbiter until this resolves.
One real fix included
mesen_crossval.lua'sMAX_FRAMESwas 900, against the harness's 1500,mesen_scenes.lua's4000 and
libretro_crossval.c's 2000. The harness comment that names the budgets which must movetogether lists the other two and omits this file, which is how it drifted. Aligned to 4000 and
the comment corrected.
It is a latent inconsistency, not the cause — 4000 frames times out exactly as 900 does — and the
commit says so rather than implying a fix.
Docs, one Lua constant, one comment. No generator or ROM change.
🤖 Generated with Claude Code
Summary
The change documents a pre-existing Mesen2 headless-runner hang. The hang triggers with the v1.25.0 cartridge image and persists through 4000 frames. It prevents
DONE = 0xA5andACSNresult markers from being observed. The diagnosis is false if the runner completes, exposes the markers, or the hang depends on the frame budget.AccuracySNES dossier changes
A5.18assertion from impossible to borderline and keeps it parked.mesen_crossval.luawith 4000 frames.No emulator-core observable behavior changes.