v2.7.0 is the submission: an email to newcores@misterfpga.org pointing a MiSTer-devel reviewer at the sibling repository. The contributing page states the bar for AI-assisted code verbatim — "Fully AI generated code should meet a minimum reasonable bar for readability and include some evidence of quality and accuracy testing." This project's evidence is real: 142 co-simulation gates, a mutation record for each, seven rung documents that each state what the rung cannot verify. And tb/regress.sh says in its own header that it "is NOT a CI gate and cannot be", so all of that was a set of documents describing checks a reader cannot run.
This release makes those claims checkable, and it opens on a finding that was carried for eight releases as a matter of style.
The .rbf name this core shipped would have distributed nothing
Distribution_MiSTer's builder strips a datecode by taking the filename stem's last nine characters and requiring _ followed by exactly eight digits; uniq_files_with_stripped_date() then continues — skips outright — any file that yields none. RustyNES_MiSTer-v2.6.13.rbf yields none. It would never have been copied into the distribution and never reached a user through update_all. The failure mode is an accepted core that appears in the wiki's Cores table and ships nothing, with no error raised anywhere.
A second, independent parser applies in the firmware: Main_MiSTer/file_io.cpp's get_display_name() takes the first literal _20, requires six characters after it, truncates the display name there and treats the rest as a datecode, and DirentComp() sorts on the truncated name. Two parsers, different rules, and _YYYYMMDD is the only form that satisfies both — which is why the fix is one name rather than a preference between two.
So there are now two names, because there are two audiences and only one of them is a parser. releases/ carries RustyNES_YYYYMMDD.rbf, which is the path the distribution reads out of the repository; the GitHub releases carry that plus the version-named copy, which no upstream tool parses, because a bitstream someone has in hand should trace to the exact release and gate results that produced it. The maintainer decision of 2026-08-30 is extended rather than reversed. The datecode comes from the tag's commit rather than date, so rebuilding a tag reproduces its filename, and v2.6.14's committed bitstream was renamed, not rebuilt.
tb/check_rbf_name.py carries both parsers' rules as one test, with ten cases of which nine are mutations — including the name this repository actually shipped, and RustyNES_v2.6.14_20260904.rbf, which parses and is still wrong because it multiplies into one core entry per release.
Two of the four R1/R2 residuals were never IRQ-timing residuals
ADR 0002 has closed this residual over four sub-tests since v2.0.0 beta.3, through 21+ documented rollbacks, a two-session bounded-effort campaign and two instrumentation studies. Two of those four do not depend on when the IRQ asserts, so no lever on any axis that campaign searched could ever have moved them.
The corpus says so, twice. mmc3_test and mmc3_test_2 are the same suite twice over and the second is the revision: it ships an "MMC3 Operation" readme section the older corpus lacks entirely, and sub-test 2 of 5-MMC3 carries the identical set_test string in both while differing by one instruction — the successor inserts a second clock_counter before the first should_be_set, so its verdict no longer lands on the $C001-pending reload. The assertion was withdrawn by its author. mmc3_test_v1/6-MMC6 rests on the same withdrawn clock and is additionally the v1 corpus's alternate-revision ROM: its header names Crystalis, which mmc3_test_2's readme identifies as revision A.
Measured rather than argued: adopting the withdrawn rule makes mmc3_test_v1/5 pass and costs both scanline_timing ROMs a regression from sub-test 3 to sub-test 2 — sub-test 2 is "should occur later", so the IRQ starts arriving too early on the one ROM pair that measures when it arrives. Reverted, with the numbers recorded, because a rejected change with its measurement is a result.
T-ORACLE-001's opening claim is retracted
It says RustyNES never clocks the MMC3 counter on the pre-render line. It does. mmc3_test_2/2-details sub-test 8 is, verbatim, "Counter should be clocked 241 times in PPU frame" — 240 visible plus the pre-render line — and RustyNES passes it, as it has for every release this suite has run. The claim came from --ppu-state-trace, which that ticket's own instrument-traps section says carries no CHR address column and therefore cannot see an A12 rise at all. A trace that could not see the event was read as evidence the event did not happen.
Gates that did not exist
sys/ verbatim is now a check. It is a submission requirement — the Template's Readme says "Basically it's prohibited to change any files in this folder" — and it rested on one measurement taken at v2.6.6 plus a manual procedure nothing ran. All 57 files are pinned, catching a changed, a missing and a stray file; the third is why the directory is enumerated rather than only the manifest walked, and it is not hypothetical, since sys/README.md and sys/.gitkeep both lived there through v2.6.5. Separately re-verified against upstream: Template_MiSTer's HEAD is the pinned 3ea1134c, and a fresh clone reports 0 differences — so sys/ is both unmodified and current, measured separately, because a vendored tree can be faithful to a commit upstream has long since moved past.
The .qsf published two seed tables. They disagreed about the pinned seed's margin by 0.155 ns, and one quoted a number the current RTL cannot reproduce. Two of this project's releases have turned on a published slack figure being wrong. tb/check_qsf_seed.py asserts one table naming the assigned seed — and its own first version reported four tables in a file with one, matching prose that mentions a seed and a slack figure, so the test is now structural and those three lines are fixtures.
bump_release.py relocates rather than drops. It took the previous release off ROADMAP.md's chain at v2.6.13 and again at v2.6.14. Reproduced in a throwaway worktree, and the previous description was wrong in a way that matters: the release is not dropped, it is filed thirty entries away beside the v2.0.0 MAJOR cut, because the insertion anchored on the first ", on " and that chain ends with one. A lineage that skips a release and names it somewhere nonsensical is worse than one that merely skips it. The CHAIN rewrite is extracted into a function, which is the load-bearing part: the selftest that covered it re-implemented the substitution inline, so the one path that had gone wrong twice was the one nothing executed.
An accuracy gate someone else can run
docs/golden-fetching.md specified this and carried Status: NOT BUILT. The nine opcode-group ROMs now export from a pinned oracle commit and compare in CI. It is a subset and the job's name says so — no PPU, no APU, no AccuracyCoin, no nestest, all of which need windows in the millions and goldens in the gigabytes. Measured before built, as the specification asks: the exporter builds in 7 s and the nine windows total 4,624 cycles. Green means green against one recorded oracle commit and never against the oracle's current behaviour, because the determinism contract covers the framebuffer and the audio and says nothing about trace-format stability.
cpu_interrupts_v2 on the DUT — the first independent interrupt oracle
docs/rung5-accuracycoin.md states the hole in its own words: "Rung 4 had blargg as an independent check and it found six defects no self-written gate could see; rung 5 has no equivalent, and that is the single most important sentence in this document." Every interrupt gate in the suite compares the DUT against RustyNES, so a shared error between them is invisible by construction.
Five single-purpose ROMs, mapper 0 — the combined ROM is mapper 1, and the singles are not, which removes MMC1 as a variable. All five pass. 5-branch_delays_irq is the sharpest: "A taken non-page-crossing branch ignores IRQ during its last clock, so that next instruction executes before the IRQ" — the exact behaviour v2.6.7 changed in the oracle from documentation reasoning alone, with no ROM adjudicating it. Alignment is recorded rather than assumed, because blargg's own readme says 2-nmi_and_brk "Occasionally fails on NES due to PPU-CPU synchronization".
One flagged claim that was not stale
Eight expired claims were corrected across both repositories. A ninth was flagged and turned out to be correct: the sibling's oracle-versus-documentation ledger was called "nine releases of entries with no changelog row", inferred from the entry numbers running to 3.43. git log shows its last commit is rung 5's own closure, and entry 3.43 was already present at v2.6.7. The changelog is right; what it needed was for its silence to be legible. Re-measure before correcting, including when the thing being corrected is an absence.
What this release does not claim
No hardware has run the bitstream. A booting core, a synced display, audible audio and a working controller are not claimed. Rung 6 stays open, confirmed by checking the USB bus, serial devices, removable block devices and mounts rather than assumed — and by maintainer decision taken while planning this release, v2.7.0 now waits for a board rather than submitting a core nothing has run. docs/bringup.md is written so that when one arrives, rung 6 costs a session.
Four properties remain unverified by construction: the palette, the video timing constants, the audio's absolute level, and band-limiting. The PPU gate compares the pre-palette index and the APU gate compares per-channel integer levels, deliberately, and the price of that partition is that all four sit downstream of every gate in the ladder.
The emulation core is unchanged — the mmc3.rs edits are #[ignore] reasons and the sibling's .sv edits are comments — so AccuracyCoin 141/141 (RAM decoder) and nestest 0-diff hold by construction.