Skip to content

feat: v2.2.9 "Studio II" — TAStudio wiring, .bk2 playback, detachable tool windows - #346

Merged
doublegate merged 29 commits into
mainfrom
feat/v2.2.9-studio-ii-tas-ux
Aug 5, 2026
Merged

feat: v2.2.9 "Studio II" — TAStudio wiring, .bk2 playback, detachable tool windows#346
doublegate merged 29 commits into
mainfrom
feat/v2.2.9-studio-ii-tas-ux

Conversation

@doublegate

@doublegate doublegate commented Aug 4, 2026

Copy link
Copy Markdown
Owner

v2.2.9 "Studio II" — TAS/movie wiring + detachable tool windows

Fourth step of the v2.2.6 → v2.3.0 NESdev-remediation line. Addresses three
forum items: TAStudio piano-roll edits that never reached the emulator, .bk2
movies that imported but didn't play back, and tool windows trapped inside the
main OS window on Windows 10.

Stacked on #345 (v2.2.8 "Aperture II"). Based on feat/v2.2.8-aperture-ii-video;
retargets to main and rebases once #345 merges. The v2.2.8 commits are not part
of this PR's review surface.

Windowing needs an on-device check. Detached tool windows use egui
multi-viewport (real OS windows). The mechanism compiles + clippy-passes on native
and both wasm feature sets, but the multi-window behavior itself is best confirmed
on a desktop — ideally the Windows-10 host from the report.

Frontend-only — core untouched

Nothing here touches emulation, so the deterministic chip stack, save-states, and
every golden vector are byte-identical (AccuracyCoin 141/141, nestest 0-diff).

Fixed

  • TAStudio piano-roll edits drive the emulator. handle_tas_requests mutated
    TasEditor::input_log only and never re-seeked the Nes, so a cell edit was
    invisible until an unrelated seek. It now re-derives through TasEditor::seek
    after the batch — the path the scripting bridge (apply_tas_commands) already used.
  • .bk2 playback honors the movie's LogKey column order. bk2_interop mapped
    columns by a fixed built-in order and ignored the LogKey: header, so BizHawk
    movies with a different column order drove the wrong buttons. It now parses the
    real LogKey: order (standard-order fallback), and import parse errors surface on
    the on-screen status bar instead of only eprintln!.

Added

  • Detachable / floating tool windows (native). A shared detachable_window
    helper gives each tool panel a "⧉ Detach" button that pops it into a real OS
    window (show_viewport_immediate) with a "⧉ Reattach" affordance; 17 panels are
    routed through it. Native-only — egui multi-viewport needs winit multi-window, so
    on wasm panels stay docked in an egui::Window (unchanged).

Checks

native clippy -D warnings (sweeper + pre-commit), wasm32 clippy -D warnings
green on both the default and wasm-canvas feature sets
(the detach path is
#[cfg]-gated native-only; a wasm-scoped allow/discard keeps unused_variables

  • needless_pass_by_ref_mut clean), cargo fmt, markdownlint, cargo check --workspace. Docs: STATUS/README/AGENTS/CHANGELOG + docs/frontend.md.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added detachable tool panels with reattach support; WASM panels remain docked.
    • TAStudio edits now update playback deterministically.
    • .bk2 playback honors declared LogKey ordering and accepts extra columns.
    • OAM sprite entries now show X coordinates and support edit-mode selection.
  • Bug Fixes

    • Improved movie-import status messages and TAStudio seeking behavior.
  • Documentation

    • Updated v2.2.9 release, provenance, and licensing documentation.
  • Chores

    • Updated licensing to GPL-3.0-or-later and consolidated release license files.
    • Updated displayed coverage to 141/141 and 172 mapper families.

Copilot AI lite review requested due to automatic review settings August 4, 2026 21:19
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 29 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3ffdf676-5b8b-4170-b1d5-9bd5d6f21177

📥 Commits

Reviewing files that changed from the base of the PR and between 5a202e7 and c96dba3.

📒 Files selected for processing (6)
  • .gitignore
  • CHANGELOG.md
  • crates/rustynes-core/src/bk2_interop.rs
  • docs/DOCUMENTATION_INDEX.md
  • docs/originality-and-provenance.md
  • docs/tooling/oracle-tooling-setup.md
📝 Walkthrough

Walkthrough

RustyNES v2.2.9 changes the project license to GPL-3.0-or-later, records source provenance, updates release metadata, fixes .bk2 LogKey handling and TAStudio synchronization, and adds shared detachable debugger-window support.

Changes

RustyNES v2.2.9

Layer / File(s) Summary
GPL relicensing and provenance
LICENSE, NOTICE, README.md, Cargo.toml, docs/adr/..., docs/originality-and-provenance.md, docs/provenance-failure-postmortem.md, crates/...
The project now declares GPL-3.0-or-later. License files, attribution, provenance records, package metadata, contribution terms, SPDX headers, and displayed license text were updated.
v2.2.9 release records and packaging
.github/workflows/release.yml, AGENTS.md, CHANGELOG.md, README.md, docs/STATUS.md, docs/frontend.md, SUPPORT.md
Release records identify v2.2.9 “Studio II”. Release archives now include LICENSE.
LogKey-aware .bk2 playback
crates/rustynes-core/src/bk2_interop.rs
.bk2 parsing follows declared LogKey column order, preserves ignored and empty columns, accepts trailing columns, and rejects short groups. Tests cover reordered and extended inputs.
TAStudio synchronization and movie status
crates/rustynes-frontend/src/app.rs
Movie import outcomes now appear in the UI status line. Batched TAStudio input and timeline edits trigger one deterministic re-seek.
Detachable debugger windows
crates/rustynes-frontend/src/debugger/*
A shared detachable_window helper supports native detached viewports and reattachment across debugger panels. WASM keeps panels docked. The OAM panel also displays sprite X coordinates.
User-facing release metadata
crates/rustynes-frontend/src/cli.rs, crates/rustynes-frontend/src/ui_shell.rs, crates/rustynes-libretro/rustynes_libretro.info, android/app/src/main/res/...
CLI, About dialogs, Android strings, and libretro metadata now report GPL-3.0-or-later and updated release metrics.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 8 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Changelog Entry For User-Visible Changes ⚠️ Warning User-facing TAStudio, .bk2 playback, and detachable-window changes are documented under [2.2.9], while [Unreleased] contains only provenance documentation. Add the TAS, .bk2 playback, and detachable-window changes to CHANGELOG.md under [Unreleased].
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary TAStudio, .bk2 playback, and detachable tool-window changes in the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Docs-As-Spec Sync ✅ Passed The PR changes only comments and provenance paths in rustynes-cpu/ppu/apu/mappers; the diff audit found 0 non-comment changed lines, so no chip behavior requires matching docs.
No Unwrap/Expect/Panic On Untrusted Input ✅ Passed The PR adds no production unwrap(), expect(), or panic!() calls; four new expect() calls are inside #[cfg(test)] tests, and .bk2 parsing returns Result<..., Bk2Error>.
Safety Comment On New Unsafe Blocks ✅ Passed The full base-to-tip diff adds no Rust line containing unsafe or SAFETY:; per-file unsafe token counts are unchanged, so no new unsafe block or unsafe fn requires a comment.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v2.2.9-studio-ii-tas-ux

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.

❤️ Share

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Not ready to approve

The new detachable_window helper drops existing per-panel window defaults (size/position/resizability), which is a user-visible regression for several tool panels.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR bumps RustyNES to v2.2.9 “Studio II” and implements three frontend-focused UX fixes: TAStudio edits are now applied to the running emulator, BizHawk .bk2 playback now honors LogKey column order, and debugger/tool panels can detach into native OS windows via egui multi-viewport.

Changes:

  • Added a shared detachable_window helper and routed many debugger/tool panels through it to enable native multi-window detaching.
  • Updated .bk2 import to parse and apply the declared LogKey: column order (with fallback behavior) and added regression tests.
  • Updated release/version documentation and workspace versioning to 2.2.9.
File summaries
File Description
README.md Updates “Current Release” text to v2.2.9 and summarizes the Studio II changes.
docs/STATUS.md Updates the status header to reflect v2.2.9 as current release.
docs/frontend.md Documents detachable tool windows as shipped in v2.2.9 and updates deferred list accordingly.
crates/rustynes-frontend/src/debugger/trace_panel.rs Routes Trace panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/rom_info_panel.rs Routes ROM Info panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/replay_panel.rs Routes Replay/TAS panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/ppu_panel.rs Routes PPU panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/oam_panel.rs Routes OAM panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/nsf_panel.rs Routes NSF panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/mod.rs Introduces detachable_window, adds detached_panels tracking, and updates panel call sites.
crates/rustynes-frontend/src/debugger/memory_compare_panel.rs Routes Memory Compare panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/mapper_panel.rs Routes Mapper panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/input_miniatures_panel.rs Routes Input Display panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/game_db_panel.rs Routes ROM Database panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/event_panel.rs Routes Event Viewer panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/doc_panel.rs Routes Documentation panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/cheat_panel.rs Routes Cheats panel through detachable_window and threads detached state on both native/wasm.
crates/rustynes-frontend/src/debugger/audio_mixer.rs Routes Audio Mixer panel through detachable_window and threads detached state.
crates/rustynes-frontend/src/debugger/apu_panel.rs Routes APU panel through detachable_window and threads detached state.
crates/rustynes-core/src/bk2_interop.rs Implements LogKey: parsing for per-port column mapping and updates input parsing + tests.
CHANGELOG.md Adds v2.2.9 release notes describing the new behaviors and constraints.
Cargo.toml Bumps workspace package version to 2.2.9.
Cargo.lock Updates crate versions to 2.2.9 across workspace packages.
Review details
  • Files reviewed: 27/28 changed files
  • Comments generated: 1
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread crates/rustynes-frontend/src/debugger/mod.rs Outdated
doublegate added a commit that referenced this pull request Aug 4, 2026
…ilot #346)

Copilot flagged a real UX regression in the v2.2.9 detachable-window
conversion: routing every tool panel through the shared `detachable_window`
helper dropped each panel's bespoke `egui::Window` builder options —
`default_pos`, `default_size`, `default_width` / `min_width`, and
`resizable(false)` on ROM Info / Input Display / ROM Database / Performance.
Losing the `default_pos` values in particular collapsed the debugger's designed
workspace layout into egui's default overlap cascade on first open, and four
fixed-size panels silently became resizable.

`detachable_window` now takes a `WindowCfg { default_pos, default_size,
default_width, min_width, resizable }` (all `Option`, `Copy + Default`) and
applies each set field to the docked `egui::Window`; all 19 call sites (18
panels; `cheat_panel` has a native + a wasm variant) pass back their exact prior
values, so first-open placement/size and the four non-resizable panels are
restored. The config applies to the docked form only — a detached panel is a
real OS window the window manager sizes and places (egui persists the docked
window's own position/size by id after first open, so `WindowCfg` only seeds the
first appearance). Native + wasm32 `clippy -D warnings` clean on both feature
sets.

Also (proactive, matching the CodeRabbit finding already fixed on #345): the
v2.2.4 entry in the AGENTS.md lineage paragraph still called v2.2.4 "the current
release" — reworded to point at the actual current-release paragraph so
AGENTS.md carries a single current-release record.

Frontend-only; the emulation core, AccuracyCoin 141/141, and nestest 0-diff are
untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Base automatically changed from feat/v2.2.8-aperture-ii-video to main August 4, 2026 21:44
doublegate and others added 4 commits August 4, 2026 17:45
…2 import (v2.2.9)

Two of the v2.2.9 'Studio II' items, both objectively verified:

TAStudio inputs now drive the emulator. handle_tas_requests (the piano-roll panel
path) only mutated the editor's input_log on a SetInput and never re-derived the
running Nes, so a cell edit looked disconnected from emulation (the NESdev-forum
'TAStudio inputs do not seem to be connected up' report). It now tracks an
input_dirty flag across the batch and does a single deterministic re-seek to the
cursor afterward, exactly like the scripting path (apply_tas_commands). InsertFrame
/ DeleteFrame / StampMacro also mark dirty; Seek / CreateBranch / LoadBranch reseat
the Nes themselves.

.bk2 import honors the LogKey column order. The parser ignored the LogKey: line and
mapped pad columns by fixed U D L R S s B A position, so a BizHawk movie authored
with a different column order or extra columns mapped every button to the wrong bit
('.bk2 did not play back'). parse_log_key now reads the per-port column order from
the LogKey (# groups, | columns), maps each column by its button name (ignoring the
'Pn ' prefix), and falls back to the standard order when a group is truncated/exotic
(preserving the existing tests). parse_pad maps by that column list and tolerates a
group LONGER than the modeled columns (extra buttons like a mic are ignored). A new
test proves a non-standard order + an extra column.

.bk2 import feedback is on-screen. handle_movie_import surfaced every outcome via
eprintln! to a terminal nobody sees (so a failed import looked like nothing
happened). It now sets the on-screen status line for each path (no ROM, parse error,
wrong-ROM seek failure, success) via StatusMessage. Consolidated the file's nine
per-function StatusMessage imports into one module-level use.

Verification: bk2 tests 7/7 (incl. the new order test), rustynes-frontend 464/464,
core no_std cross-compile clean, clippy -D warnings + fmt clean on both crates.
Remaining v2.2.9 item: floating tool windows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses the NESdev-forum report that tool windows are trapped inside the
main OS window on Windows 10: every debugger/tool panel used
`egui::Window::new(...)` inside the single central viewport, so it could never
leave the host window.

Adds a shared `detachable_window` helper in `debugger/mod.rs` that gives each
panel a "⧉ Detach" button. Detached, the panel renders in a real OS window via
`ctx.show_viewport_immediate` (the same egui multi-viewport mechanism
`basic_bot_panel` already used) with a "⧉ Reattach" button; the OS window's
close button reattaches too. A `DebuggerOverlay::detached_panels:
HashSet<&'static str>` (keyed by each panel's stable id) tracks which panels
are floating across frames.

**Native-only by construction.** egui multi-viewport needs winit multi-window,
absent on wasm, so the detached branch and the Detach button are
`#[cfg(not(target_arch = "wasm32"))]`; on wasm the panel always renders docked
in an `egui::Window`, unchanged. The helper carries a wasm-scoped
`allow(clippy::needless_pass_by_ref_mut)` plus a `let _ = (&detached, id)`
discard so both the rustc `unused_variables` and clippy `needless_pass_by_ref_mut`
lints stay green there without desyncing the native signature (verified:
`cargo clippy -p rustynes-frontend --target wasm32-unknown-unknown --lib --bins`
clean for both the default and `wasm-canvas` feature sets).

17 panels are routed through the helper (PPU, OAM, APU, Memory, Event Viewer,
NSF, Mapper, Watch, Trace, Cheats [native + wasm cfg variants], ROM Database,
Performance, Documentation, Input Display, Audio Mixer, Replay/TAS, Memory
Compare, ROM Info), each dropping its bespoke `.resizable()/.default_pos()/
.default_size()/.min_width()` builder options for the shared affordance. Panels
whose `show()` returns a value (`cpu_panel`) or that already own multi-window /
config-heavy bodies (settings, netplay, cheevos, input-rebind, tastudio,
basic_bot) are intentionally left for a follow-up.

Frontend-only — the deterministic core, save-states, and every golden vector
are untouched (AccuracyCoin 141/141, nestest 0-diff).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Version bump 2.2.8 → 2.2.9 (workspace `version`, Cargo.lock) and the
docs-as-spec sync for the "Studio II" release — the fourth step of the
v2.2.6 → v2.3.0 NESdev-remediation line, capping the TAStudio-wiring, `.bk2`
playback, and detachable-tool-window work committed earlier on this branch.

- **CHANGELOG.md** — new `[2.2.9]` section (Fixed: TAStudio piano-roll edits
  now drive the emulator, `.bk2` playback honors the `LogKey` column order;
  Added: detachable/floating tool windows across 17 panels, native-only).
- **docs/STATUS.md** (single source of truth) — current-release lead reset to
  v2.2.9, demoting v2.2.8 to "Built on".
- **README.md** — Current Release lead updated to v2.2.9.
- **AGENTS.md** — both the top current-release block and the operating-note
  paragraph lead with v2.2.9; the "never claim a version later than …" guard
  and the v2.2.6 → v2.3.0 line-summary bump to mark v2.2.8/v2.2.9 shipped.
- **docs/frontend.md** — detachable multi-viewport tool windows moved out of
  the Deferred list into shipped (v2.2.9), with the `detachable_window` /
  `show_viewport_immediate` mechanism noted.

Frontend-only across the whole release, so the deterministic core, save-states,
and every golden vector are byte-identical: **AccuracyCoin 141/141**, nestest
0-diff. The detached-window behavior itself awaits an on-device (ideally
Windows-10) visual check; the mechanism compiles + clippy-passes on native and
both wasm feature sets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ilot #346)

Copilot flagged a real UX regression in the v2.2.9 detachable-window
conversion: routing every tool panel through the shared `detachable_window`
helper dropped each panel's bespoke `egui::Window` builder options —
`default_pos`, `default_size`, `default_width` / `min_width`, and
`resizable(false)` on ROM Info / Input Display / ROM Database / Performance.
Losing the `default_pos` values in particular collapsed the debugger's designed
workspace layout into egui's default overlap cascade on first open, and four
fixed-size panels silently became resizable.

`detachable_window` now takes a `WindowCfg { default_pos, default_size,
default_width, min_width, resizable }` (all `Option`, `Copy + Default`) and
applies each set field to the docked `egui::Window`; all 19 call sites (18
panels; `cheat_panel` has a native + a wasm variant) pass back their exact prior
values, so first-open placement/size and the four non-resizable panels are
restored. The config applies to the docked form only — a detached panel is a
real OS window the window manager sizes and places (egui persists the docked
window's own position/size by id after first open, so `WindowCfg` only seeds the
first appearance). Native + wasm32 `clippy -D warnings` clean on both feature
sets.

Also (proactive, matching the CodeRabbit finding already fixed on #345): the
v2.2.4 entry in the AGENTS.md lineage paragraph still called v2.2.4 "the current
release" — reworded to point at the actual current-release paragraph so
AGENTS.md carries a single current-release record.

Frontend-only; the emulation core, AccuracyCoin 141/141, and nestest 0-diff are
untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@doublegate
doublegate force-pushed the feat/v2.2.9-studio-ii-tas-ux branch from 16e18ac to 7aa5836 Compare August 4, 2026 21:46
doublegate and others added 3 commits August 4, 2026 18:15
…k of GPL emulators

RustyNES incorporates and is derived from code from GPL-licensed NES emulators.
It is therefore a derivative work distributable only under the GPL, and this
commit relicenses it from `MIT OR Apache-2.0` to **GPL-3.0-or-later**, credits
the derived-from sources per subsystem, and withdraws the incorrect "no GPL
source incorporated" position taken in v2.2.5 "Colophon".

Context. A NESdev community review found that the codebase contains bugs,
constants, variable names, code ordering, and comments referencing specific
upstream files, functions, and line numbers that go well beyond using an
emulator as a testing oracle. That is correct. The project's own in-source
comments, before a v2.2.5 edit reworded them, said as much: "Faithful port of
Mesen2's `ProcessSpriteEvaluation` (`NesPpu.cpp:1015-1141`)", "Ported bit-for-bit
from puNES `JV001.c`", "numeric tables ported verbatim from Bisqwit's C", and
~12 "Ported from Mesen2 `<file>.h`" mapper comments. v2.2.5 reframed that code as
"oracle cross-checks" and kept a permissive license the combined work was not
entitled to use. Laundering GPL code through AI tooling does not change its
license, and responsibility for what landed in the tree rests with the project.

Derived-from sources and their licenses (full file-by-file table in
docs/originality-and-provenance.md Section 1):
  - Mesen2 (GPL-3.0-or-later): CPU unstable-store opcodes; the PPU
    sprite-evaluation FSM + OAM-data-bus model; ~15 mapper boards (Bandai EEPROM,
    JY Company, Waixing, Sachen, Txc, NTDEC, Kaiser, MMC3 variants, FK23C,
    CoolBoy); the Bisqwit NTSC filter tables; the UNIF tables; the debug-symbol
    importer; the PGO harness.
  - puNES (GPL-2.0-or-later): JV001 / mapper 147 (bit-for-bit); the FDS per-CRC
    drive-timing table.
  - FCEUX (GPL-2.0-or-later): UNIF handling; some mapper banking.
  - Nestopia UE (GPL-2.0-or-later): FME-7 / 5B audio detail.
Every upstream grants "or (at your option) any later version", so the
GPL-2.0-or-later material upgrades to v3 and GPL-3.0-or-later is the correct,
consistent expression for the combined work. GeraNES (GPL-3.0-only) was used as
an oracle only, with no code derived, so it does not force `-only`.

Changes:
  - LICENSE is now the GPLv3 text; LICENSE-MIT and LICENSE-APACHE are removed;
    the workspace + rustynes-cheevos `license` fields become GPL-3.0-or-later;
    deny.toml allows GPL-3.0-or-later for the project's own crates (cargo-deny
    `check licenses` = ok); release.yml packages LICENSE instead of the two
    removed files.
  - docs/originality-and-provenance.md is rewritten to lead with the derivation
    table and the derivative-work declaration; NOTICE attributes each GPL
    upstream and the code derived from it; README, AGENTS, CONTRIBUTING, SUPPORT,
    ROADMAP, the in-app About/CLI/doc-panel strings, the Android about_body
    (EN + ES), and the libretro `.info` license field all state GPL-3.0-or-later.
  - New ADR 0036 records the decision, the SPDX rationale, and the GPLv3/App-Store
    distribution caveat. The scattered "port of" comments are deliberately NOT
    restored (they were imprecise; the audited derivation table supersedes them),
    but the derivation is now stated plainly and completely.
  - Incorporated permissive components (emu2413/MIT, TriCNES/MIT, rcheevos/MIT,
    blip_buf/LGPL-2.1-or-later, fonts) are GPL-compatible and keep their notices.

Zero emulation-core behavior change: AccuracyCoin holds 141/141 and nestest is
0-diff by construction. This is a licensing and documentation correction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Carry-over fix: the version badge still read v2.2.8 after the v2.2.9 doc bump.

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

Follows the v2.2.9 relicense: now that the accurate license and attribution are
established, mark the derived source itself. Each of the 23 files that contains
code derived from a GPL emulator gains a top-of-file header:

    // SPDX-License-Identifier: GPL-3.0-or-later
    //
    // Provenance: <what is derived, from which upstream file/function>. See
    // docs/originality-and-provenance.md (Section 1) and NOTICE ...

so the license and the specific upstream are discoverable at the point of use —
e.g. `rustynes-ppu/src/ppu.rs` names Mesen2 `NesPpu.cpp`
(`ProcessSpriteEvaluation` / `ReadSpriteRam`) plus the TriCNES (MIT) octal-latch
model; `rustynes-mappers/src/fds.rs` names puNES `fds.c`;
`rustynes-frontend/src/ntsc_bisqwit.rs` records the verbatim-ported Bisqwit
tables via Mesen2. The ~15 Mesen2-derived mapper boards, the CPU unstable-store
opcodes, the emu2413/blip_buf audio, the CRT-shader reimplementations, the debug-
symbol importer, and the PGO harness are all likewise marked.

This is the accurate replacement for the old scattered, imprecise per-line "port
of" comments (not restored verbatim); the SPDX + provenance headers plus the
audited §1 derivation table are the discoverable record. CHANGELOG, ADR 0036, and
docs/originality-and-provenance.md §8 are updated to describe this approach.

Comments only — `cargo fmt --all --check` clean, `cargo check --workspace`
compiles, zero behavior change (AccuracyCoin 141/141, nestest 0-diff).

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🤖 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 `@android/app/src/main/res/values/strings.xml`:
- Line 188: The AccuracyCoin release counts are outdated in the user-facing
accuracy text. Update about_body in android/app/src/main/res/values/strings.xml
at lines 188-188 and android/app/src/main/res/values-es/strings.xml at lines
168-168 from 139/139 to 141/141, and update the frontend accuracy text in
crates/rustynes-frontend/src/debugger/doc_panel.rs at lines 1192-1192 to 100%
(141/141).

In `@CHANGELOG.md`:
- Around line 75-80: Count the actual detachable_window call sites, then use
that verified count and complete panel list consistently in CHANGELOG.md lines
75-80, AGENTS.md lines 30 and 188, and docs/frontend.md lines 1743-1747; update
each affected summary/specification without removing a valid panel name or
introducing inconsistent counts.

In `@crates/rustynes-core/src/bk2_interop.rs`:
- Around line 365-370: Update the parsing logic around the groups and cols
closures to preserve empty interior fields while removing only the
syntax-defined terminal delimiter, so empty controller columns and console
groups retain their positional indexes. Ensure malformed or unsupported layouts
are rejected with Bk2Error rather than silently remapped, and add regression
tests covering an empty controller column and empty console group.

In `@crates/rustynes-frontend/src/app.rs`:
- Around line 3036-3044: Before handling TasRequest::CreateBranch or
TasRequest::LoadBranch, check input_dirty and call ed.seek(nes, ed.cursor()) to
flush pending edits; only then invoke create_branch or load_branch, preserving
the flushed state instead of unconditionally clearing the flag. Add a regression
covering pending edit requests followed by each branch operation in the same
batch.

In `@crates/rustynes-frontend/src/debugger/event_panel.rs`:
- Around line 98-115: Update detachable_window to apply applicable WindowCfg
fields, including default size/position and resizable, when constructing the
detached native ViewportBuilder, while preserving docked-window behavior; verify
this on native desktop targets. The affected call sites require no direct
changes: crates/rustynes-frontend/src/debugger/event_panel.rs:98-115,
crates/rustynes-frontend/src/debugger/input_miniatures_panel.rs:130-147,
crates/rustynes-frontend/src/debugger/mapper_panel.rs:41-60,
crates/rustynes-frontend/src/debugger/perf_panel.rs:195-214,
crates/rustynes-frontend/src/debugger/ppu_panel.rs:122-141,
crates/rustynes-frontend/src/debugger/replay_panel.rs:81-99,
crates/rustynes-frontend/src/debugger/rom_info_panel.rs:71-90,
crates/rustynes-frontend/src/debugger/trace_panel.rs:60-78, and
crates/rustynes-frontend/src/debugger/watch_panel.rs:456-478.

In `@crates/rustynes-frontend/src/debugger/mod.rs`:
- Around line 303-320: Remove or gate the Detach action and its
show_viewport_immediate path until secondary viewport rendering is implemented.
Before re-enabling it, update DebuggerOverlay::render and
DebuggerOverlay::render_shell to own child winit windows, route their
input/output, and render secondary viewport output so the detached panel remains
interactive.

In `@docs/adr/0036-relicense-gplv3-derivative-work.md`:
- Around line 95-97: Update the accuracy statement in the ADR to describe
AccuracyCoin’s 141/141 and nestest’s 0-diff results as verified release-check
evidence rather than “by construction,” and link to the authoritative
docs/STATUS.md pass counts.
- Around line 78-82: Revise the historical-release language in
docs/adr/0036-relicense-gplv3-derivative-work.md (lines 78-82) and
docs/originality-and-provenance.md (lines 98-102) to preserve immutable
snapshot/tag history without stating or implying that prior unauthorized
permissive license grants remain valid. Use legally reviewed wording
consistently in both documents, while retaining the GPL-3.0-or-later
applicability from v2.2.9 onward.

In `@docs/originality-and-provenance.md`:
- Around line 43-62: Revise Section 1 so its introductory claims apply only to
rows derived from GPL-licensed emulators and do not include the BSD/Apache,
LGPL, or MIT components. Separate the ares, blip_buf, emu2413, and shader
reimplementation entries into appropriately labeled non-GPL or
visual-reimplementation provenance tables/sections, and update the licensing
rationale to match each table’s documented relationship and license.

In `@README.md`:
- Line 12: Update the version citation in the README’s Current Release section,
including the matching citation at the additional referenced location, from
2.2.8 to 2.2.9 so it aligns with the release badge and current release metadata;
alternatively, explicitly mark it as historical if it must remain unchanged.

In `@SUPPORT.md`:
- Line 109: Synchronize release metadata across SUPPORT.md:97 and
crates/rustynes-libretro/rustynes_libretro.info:6: update the current-release
description from v2.0.4 “Harbor” to v2.2.9 “Studio II”, and change the Libretro
display_version from v2.2.5 to v2.2.9 unless an independently documented
Libretro version contract is added instead.
🪄 Autofix

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: eb00d6a6-da48-4c8c-9089-fca3cd6173c7

📥 Commits

Reviewing files that changed from the base of the PR and between b05ad53 and 53df792.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !Cargo.lock
📒 Files selected for processing (44)
  • .github/workflows/release.yml
  • AGENTS.md
  • CHANGELOG.md
  • CONTRIBUTING.md
  • Cargo.toml
  • LICENSE
  • LICENSE-APACHE
  • LICENSE-MIT
  • NOTICE
  • README.md
  • ROADMAP.md
  • SUPPORT.md
  • android/app/src/main/res/values-es/strings.xml
  • android/app/src/main/res/values/strings.xml
  • crates/rustynes-cheevos/Cargo.toml
  • crates/rustynes-core/src/bk2_interop.rs
  • crates/rustynes-frontend/src/app.rs
  • crates/rustynes-frontend/src/cli.rs
  • crates/rustynes-frontend/src/debugger/apu_panel.rs
  • crates/rustynes-frontend/src/debugger/audio_mixer.rs
  • crates/rustynes-frontend/src/debugger/cheat_panel.rs
  • crates/rustynes-frontend/src/debugger/doc_panel.rs
  • crates/rustynes-frontend/src/debugger/event_panel.rs
  • crates/rustynes-frontend/src/debugger/game_db_panel.rs
  • crates/rustynes-frontend/src/debugger/input_miniatures_panel.rs
  • crates/rustynes-frontend/src/debugger/mapper_panel.rs
  • crates/rustynes-frontend/src/debugger/memory_compare_panel.rs
  • crates/rustynes-frontend/src/debugger/memory_panel.rs
  • crates/rustynes-frontend/src/debugger/mod.rs
  • crates/rustynes-frontend/src/debugger/nsf_panel.rs
  • crates/rustynes-frontend/src/debugger/oam_panel.rs
  • crates/rustynes-frontend/src/debugger/perf_panel.rs
  • crates/rustynes-frontend/src/debugger/ppu_panel.rs
  • crates/rustynes-frontend/src/debugger/replay_panel.rs
  • crates/rustynes-frontend/src/debugger/rom_info_panel.rs
  • crates/rustynes-frontend/src/debugger/trace_panel.rs
  • crates/rustynes-frontend/src/debugger/watch_panel.rs
  • crates/rustynes-frontend/src/ui_shell.rs
  • crates/rustynes-libretro/rustynes_libretro.info
  • deny.toml
  • docs/STATUS.md
  • docs/adr/0036-relicense-gplv3-derivative-work.md
  • docs/frontend.md
  • docs/originality-and-provenance.md
💤 Files with no reviewable changes (2)
  • LICENSE-APACHE
  • LICENSE-MIT

Comment thread android/app/src/main/res/values/strings.xml Outdated
Comment thread CHANGELOG.md Outdated
Comment thread crates/rustynes-core/src/bk2_interop.rs Outdated
Comment thread crates/rustynes-frontend/src/app.rs
Comment thread crates/rustynes-frontend/src/debugger/event_panel.rs
Comment thread docs/adr/0036-relicense-gplv3-derivative-work.md
Comment thread docs/adr/0036-relicense-gplv3-derivative-work.md Outdated
Comment thread docs/originality-and-provenance.md
Comment thread README.md
Comment thread SUPPORT.md
doublegate and others added 2 commits August 4, 2026 18:53
…-mortem

Two things: the maintainer-directed forensic analysis of the GPL-provenance
failure, and the CodeRabbit review pass on the v2.2.9 PR.

**docs/provenance-failure-postmortem.md (new).** A complete, evidence-cited
reconstruction of how RustyNES came to incorporate lifted GPL emulator code
despite a black-box-only instruction: the timeline across RustyNES_v2 (the
private "engine stack" where the porting happened, 2026-05, Opus 4.7) and the
2026-06-13 transplant into this repo; the two distinct failures (the port itself,
then the v2.2.5 scrubbing of the honest "port of" comments); the root causes
(GPL source on disk + accuracy-bar goal + no firewall; the guardrail post-dating
the violation; honest-at-build-time then laundered; multi-session framing
propagation; trusted AI self-attestation); and an honest accounting of what is
NOT recoverable (the RustyNES_v2 porting-era session logs are gone). The single
hardest fact: the original "Faithful port of Mesen2's ..." comments still exist
verbatim in RustyNES_v2 today — only this public repo scrubbed them. Linked from
originality-and-provenance.md §8.

**CodeRabbit #346 review (9 threads):**
- **`.bk2` LogKey empty-field bug (Major, data integrity).** `parse_log_key`
  filtered out empty positional fields, shifting later columns/groups (an empty
  console group promoted P2's map into P1; an empty interior column misaligned
  buttons so `U.A` replayed as `Up` alone). Now strips only the syntax delimiters
  and keeps interior empties; +regression test for both cases.
- **TAS branch/load ordering (correctness).** `CreateBranch` / `LoadBranch`
  cleared `input_dirty` without flushing pending edits, so a branch snapshot
  captured stale state; they now `ed.seek` to flush first.
- **`WindowCfg` -> `ViewportBuilder` (Major).** The detached branch maps default
  size / position / resizability onto the viewport, not just the docked window.
- **Multi-viewport honesty (Major).** RustyNES's frontend is a single-viewport
  `egui_winit` integration, so `show_viewport_immediate` renders the "detached"
  panel EMBEDDED in the main window rather than a separate OS window — it does not
  yet fully resolve the Windows-10 trapped-window report. Documented honestly in
  code, CHANGELOG, AGENTS.md, and docs/frontend.md; true OS-window detach is
  tracked follow-up. Corrects an overclaim.
- Doc/metadata: panel count 17 -> 18; ADR 0036 "by construction" -> verified
  release-check evidence + STATUS link; README badge/BibTeX -> v2.2.9; SUPPORT
  current-release v2.0.4 -> v2.2.9; libretro display_version -> v2.2.9; Android
  about_body 139/139 -> 141/141 and 168 -> 172 mappers (EN + ES).

Core-affecting fixes (bk2, TAS) are core/frontend only; cargo check + the bk2
tests pass. AccuracyCoin 141/141 unaffected.

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

Reconcile the root-cause framing with the maintainer's correction: the black-box
/ oracle-only instruction WAS given — the failure was that it was not mechanically
enforced (no barrier at the tool boundary; no persisted written rule in the loaded
guidance until 2026-06-13) and the porting model did not follow it. §4.1 and §4.2
reframed from "no guardrail / the guardrail post-dated the violation" to
"instruction given, neither persisted early nor enforced"; §4.5 sharpened (an
instruction the agent can silently disregard and then falsely certify is not a
control). The evidentiary caveat is unchanged: the porting-era logs are gone, so
the exact wording/timing of the spoken instruction cannot be quoted.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
crates/rustynes-core/src/bk2_interop.rs (1)

369-391: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not allocate every LogKey group.

Line 369 collects all #-separated groups, but this parser reads only the console, P1, and P2 groups. A movie with many repeated # delimiters creates one Vec<&str> entry per empty group. This can exhaust memory during import.

Consume the first three groups from the split('#') iterator. Iterator::next() preserves empty groups.

Proposed fix
-    let groups: Vec<&str> = body.split('#').collect();
-    let cols = |g: Option<&&str>| -> Vec<Option<Buttons>> {
+    let mut groups = body.split('#');
+    let _console = groups.next();
+    let cols = |g: Option<&str>| -> Vec<Option<Buttons>> {
         let mapped: Vec<Option<Buttons>> = g.map_or_else(Vec::new, |grp| {
             grp.strip_suffix('|')
                 .unwrap_or(grp)
                 .split('|')
                 .map(button_for_column)
                 .collect()
         });
@@
-    (cols(groups.get(1)), cols(groups.get(2)))
+    (cols(groups.next()), cols(groups.next()))
🤖 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 `@crates/rustynes-core/src/bk2_interop.rs` around lines 369 - 391, Replace the
groups Vec allocation in the button-column parsing flow with a split('#')
iterator, then consume exactly the console, P1, and P2 entries via successive
next() calls while preserving empty groups. Update the existing cols calls to
use those optional group references so only the required three groups are
processed and trailing delimiters cannot cause unbounded allocation.
CHANGELOG.md (2)

19-19: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove future v2.3.0 references from both release-facing documents.

The repository documents v2.2.9 as the current release and must not claim a later release.

As per coding guidelines: do not claim or document a release later than v2.2.9.

  • CHANGELOG.md#L19-L19: replace the v2.2.6 → v2.3.0 wording with neutral current-line wording.
  • SUPPORT.md#L97-L97: remove the v2.3.0 endpoint from the current-release description.
🤖 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 `@CHANGELOG.md` at line 19, Update CHANGELOG.md line 19 to replace the v2.2.6 →
v2.3.0 wording with neutral wording for the current release line, and update
SUPPORT.md line 97 to remove the v2.3.0 endpoint from the current-release
description. Ensure neither release-facing document claims a version later than
v2.2.9.

Source: Coding guidelines


28-31: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the detached-window description.

These lines say that detached tool windows use real OS windows. Lines 86-93 state that the current single-viewport integration embeds them in the main window. Keep the release overview consistent with the documented limitation.

As per path instructions: Markdown documentation must match the behavior it describes.

Proposed fix
- > **Windowing needs an on-device check.** Detached tool windows use egui
- > multi-viewport (real OS windows); the mechanism compiles and clippy-passes on
+ > **Windowing needs an on-device check.** Detached tool windows remain embedded
+ > in the main viewport; the multi-viewport mechanism compiles and clippy-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 `@CHANGELOG.md` around lines 28 - 31, Update the detached tool window
description in the release overview to state that the current single-viewport
integration embeds tool windows in the main window, matching the limitation
documented later in CHANGELOG.md. Remove the claim that detached windows
currently use real OS windows, while preserving the existing note about
on-device validation.

Source: Path instructions

🤖 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 `@AGENTS.md`:
- Line 30: Remove future-release references from AGENTS.md: at AGENTS.md lines
30-30, replace the “v2.2.6 → v2.3.0” wording with version-neutral post-v2.2.9
future-work language; at lines 188-188, remove the detailed v2.3.0 release
narrative and retain only post-v2.2.9 planning language.

In `@docs/adr/0036-relicense-gplv3-derivative-work.md`:
- Around line 62-68: The per-file provenance requirement must match the Kaiser
header. In docs/adr/0036-relicense-gplv3-derivative-work.md lines 62-68 and
docs/originality-and-provenance.md lines 244-252, retain or revise the
requirement consistently so every derived file names an exact upstream source
location or function; in crates/rustynes-mappers/src/kaiser.rs lines 1-4, add
the specific Mesen2 source path or function to the provenance header.

In `@docs/provenance-failure-postmortem.md`:
- Around line 25-28: In docs/provenance-failure-postmortem.md lines 25-28,
qualify the claims about the model deciding to match Mesen2 and partially
porting it as inference rather than established fact. In lines 89-99, clearly
distinguish maintainer testimony from recovered evidence and state when the
reasoning or exact instruction is unavailable; keep the evidentiary standard
consistent across both sections.
- Line 52: Update the 2026-06-13 RustyNES provenance entry to identify the
transplanted stack as an internal “RustyNES_v2” engine-lineage snapshot,
explicitly stating that it is not a RustyNES release. Preserve the existing
event details while avoiding any wording that presents v2.8.0 as a RustyNES
release beyond the permitted v2.2.9 documentation limit.

---

Outside diff comments:
In `@CHANGELOG.md`:
- Line 19: Update CHANGELOG.md line 19 to replace the v2.2.6 → v2.3.0 wording
with neutral wording for the current release line, and update SUPPORT.md line 97
to remove the v2.3.0 endpoint from the current-release description. Ensure
neither release-facing document claims a version later than v2.2.9.
- Around line 28-31: Update the detached tool window description in the release
overview to state that the current single-viewport integration embeds tool
windows in the main window, matching the limitation documented later in
CHANGELOG.md. Remove the claim that detached windows currently use real OS
windows, while preserving the existing note about on-device validation.

In `@crates/rustynes-core/src/bk2_interop.rs`:
- Around line 369-391: Replace the groups Vec allocation in the button-column
parsing flow with a split('#') iterator, then consume exactly the console, P1,
and P2 entries via successive next() calls while preserving empty groups. Update
the existing cols calls to use those optional group references so only the
required three groups are processed and trailing delimiters cannot cause
unbounded allocation.
🪄 Autofix

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: dd7a7a68-e916-42c5-8106-089b5b939e3a

📥 Commits

Reviewing files that changed from the base of the PR and between 53df792 and d5c0bab.

📒 Files selected for processing (37)
  • AGENTS.md
  • CHANGELOG.md
  • README.md
  • SUPPORT.md
  • android/app/src/main/res/values-es/strings.xml
  • android/app/src/main/res/values/strings.xml
  • crates/rustynes-apu/src/blip.rs
  • crates/rustynes-apu/src/opll.rs
  • crates/rustynes-core/src/bk2_interop.rs
  • crates/rustynes-cpu/src/cpu.rs
  • crates/rustynes-frontend/src/app.rs
  • crates/rustynes-frontend/src/debugger/mod.rs
  • crates/rustynes-frontend/src/debugger/source_map.rs
  • crates/rustynes-frontend/src/ntsc_bisqwit.rs
  • crates/rustynes-gfx-shaders/src/crt_stack.rs
  • crates/rustynes-libretro/rustynes_libretro.info
  • crates/rustynes-mappers/src/fds.rs
  • crates/rustynes-mappers/src/kaiser.rs
  • crates/rustynes-mappers/src/lib.rs
  • crates/rustynes-mappers/src/m016_bandai_fcg.rs
  • crates/rustynes-mappers/src/m035_jy_asic.rs
  • crates/rustynes-mappers/src/m069_sunsoft_fme7.rs
  • crates/rustynes-mappers/src/m176_bmc_fk23c.rs
  • crates/rustynes-mappers/src/m268_bmc_coolboy.rs
  • crates/rustynes-mappers/src/m513_sachen_9602.rs
  • crates/rustynes-mappers/src/mmc3_clones.rs
  • crates/rustynes-mappers/src/multicart_discrete.rs
  • crates/rustynes-mappers/src/ntdec.rs
  • crates/rustynes-mappers/src/sachen_discrete.rs
  • crates/rustynes-mappers/src/unif.rs
  • crates/rustynes-ppu/src/palette_gen.rs
  • crates/rustynes-ppu/src/ppu.rs
  • crates/rustynes-test-harness/src/bin/pgo_trainer.rs
  • docs/adr/0036-relicense-gplv3-derivative-work.md
  • docs/frontend.md
  • docs/originality-and-provenance.md
  • docs/provenance-failure-postmortem.md

Comment thread AGENTS.md
Comment thread docs/adr/0036-relicense-gplv3-derivative-work.md
Comment thread docs/provenance-failure-postmortem.md
Comment thread docs/provenance-failure-postmortem.md
doublegate and others added 6 commits August 4, 2026 19:52
…r's edits

Non-substantive cleanup of the maintainer's review edits: stripped trailing
whitespace (§1, §2, and the closing NOTE), evened out the wrap widths the inline
edits left uneven, standardized hyphen-as-dash to em-dash in the NOTE, fixed one
phrase that didn't parse ("finally did baseline" -> "finally did become the
baseline"), and reconciled §5 with §4.2's "written instruction" framing (dropped
the now-inconsistent "or verbally"; the honest "cannot be quoted, logs gone"
point is unchanged). No substantive claims or the maintainer's wording/voice
were altered. markdownlint clean, no trailing whitespace.

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

Restore accurate, license-specific attribution at each genuine ported site,
replacing the v2.2.5-laundered false-independence claims that were STILL in the
shipped per-function comments (the SPDX top-of-file headers alone did not undo
them). At each ported function the comment now names the upstream file/function
and its license, instead of asserting "an independent implementation … no
third-party emulator code is incorporated" / "cross-checked … as oracles".

Corrected (18 files) — examples:
  - `cpu.rs` SH* stores → Mesen2 `NesCpu.h` (`SyaSxaAxa`), GPL-3.0-or-later
  - `ppu.rs` OAM-data-bus / sprite-eval → Mesen2 `NesPpu.cpp`
    (`ProcessSpriteEvaluation` / `ReadSpriteRam`), GPL-3.0-or-later
  - `ntsc_bisqwit.rs` → tables ported verbatim via Mesen2 `BisqwitNtscFilter`,
    GPL-3.0-or-later
  - `blip.rs` → BLEP technique derived from Shay Green's `blip_buf`,
    LGPL-2.1-or-later (our kernel is a finer 32-phase refinement)
  - mapper boards (m016 Bandai EEPROM, m035/lib JY, m176/m268 FK23C/CoolBoy,
    m513/mmc3_clones/sachen_discrete Sachen, multicart/ntdec NTDEC/Txc, kaiser
    Waixing) → their specific Mesen2 `.h` sources, GPL-3.0-or-later; CoolBoy also
    FCEUX (GPL-2.0-or-later)
  - `unif.rs` → Mesen2 `UnifLoader.cpp` (GPLv3) + FCEUX `unif.cpp` (GPLv2)
  - `sachen_discrete.rs` JV001 → puNES `JV001.c` / `mapper_147.c` (GPL-2.0-or-later)
  - `fds.rs` per-CRC drive table → puNES `src/core/fds.c` (GPL-2.0-or-later)
  - `source_map.rs` → mirrors Mesen2 `DbgImporter` (GPL-3.0-or-later)

Deliberately LEFT unchanged (no over-attribution): `opll.rs` (already honestly
"a pure-Rust port of emu2413", MIT), `pgo_trainer.rs` (honest `PGOHelper`
pattern), `palette_gen.rs` / `crt_stack.rs` (documented method / genuine
look-reimplementation, no false claim), and `m069_sunsoft_fme7.rs` (its Mesen2
mentions are genuine `_volumeLut` oracle cross-checks). Genuine oracle-comparison
mentions ("matches Mesen2", "Mesen2-independent oracle") were NOT converted.

Comments only — `cargo fmt`, `cargo check`, and `cargo clippy -D warnings`
(cpu/ppu/apu/mappers/frontend) all clean. Zero behavior change.

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

A styled PDF rendering of docs/provenance-failure-postmortem.md for the reference
corpus, at ref-docs/RustyNES_Provenance-Failure-Postmortem.pdf. Themed as a
forensic incident record — oxblood-crimson accents (severity), a charcoal serif
body (official-record readability), dark-slate evidence-table headers, and
monospace for commit hashes / file paths — with a title block, table of contents,
and page footers. 8 pages, US Letter. Built with pandoc 3.6.1 -> WeasyPrint 68.1
from the committed markdown; content is identical to the source document.

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

A forward-looking, general-purpose ruleset distilled from the provenance-failure
post-mortem: docs/ai-emulator-provenance-guardrails.md. Written to be dropped
into a project's agent instructions / permanent memory BEFORE development starts
so the same trap cannot recur — in this project or any other emulator project
using prior art (reference emulators, test ROMs).

Contents: why emulators are a special trap for AI agents (accuracy is convergent
+ references are mostly copyleft); a classification of every external input
(documentation / test ROMs / observable oracles / incorporated components) and
what each permits; the reference firewall (oracles are run and observed, never
opened and read — enforced by a denied read-path + a CI check, not by prose);
attribution on four consistent surfaces (site comment + SPDX + central table +
NOTICE) with a no-over-attribution rule; license arithmetic (the or-later grant,
combined-work copyleft, the license gate); mechanical enforcement; a
pre-development checklist; a paste-ready guardrail block for CLAUDE.md/AGENTS.md;
a remediation runbook (do NOT scrub); and a red-flags table of the thoughts that
precede the failure. Intended for sharing as NESdev-community best-guidance.

Cross-linked from the post-mortem's §7 (Lessons and prevention) as the actionable
counterpart. markdownlint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Redesign per maintainer feedback ("less harsh on the eyes"): a cool blue/teal
palette (navy title, blue headings + rules, teal accents) on soft cool-slate
text, with RED reserved only for genuine takeaways; humanist sans-serif
throughout (Fira Sans, with FiraCode for code) at a comfortable weight/leading;
a two-column layout for an ideal ~55-60 character measure (per readability
research); and the maintainer's closing NOTE set apart in a full-width
light-blue "Maintainer's Statement" box. Compacted from 8 pages to 4 (US Letter).
Built pandoc 3.6.1 -> a bs4 DOM assembler (full-width title + evidence table
between 2-column prose groups) -> WeasyPrint 68.1. Content unchanged.

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

Generalize docs/ai-emulator-provenance-guardrails.md so it stands on its own as a
community resource — no references to any specific project or its source-tree
documents, and applicable to emulation of ANY console, not just the NES:

- Reworded the opening to describe the failure pattern generically (no specific
  project, no cross-link to a project post-mortem) and to state it applies to
  NES / SNES / Genesis / Game Boy / N64 / PlayStation / arcade / etc.
- Broadened the reference-emulator examples across consoles (Mesen2/FCEUX,
  bsnes/Mesen-S, Genesis Plus GX/BlastEm, SameBoy/mGBA, ares/higan/MAME, …) in
  the bucket table and the paste-ready block.
- Genericized the example provenance comment, the central-table / provenance-doc
  filenames (`PROVENANCE.md` or equivalent), the firewall-check grep, and the
  "matches reference X" over-attribution example.
- Removed the "case study" / "this project" phrasings and the two links to
  project source-tree docs (post-mortem, derivation table); the remediation and
  closing now speak generally.

Content and rules are unchanged; only the framing is now project-neutral and
multi-console. markdownlint clean.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/rustynes-cpu/src/cpu.rs`:
- Around line 877-882: Correct the central provenance table so Txc/TxcChip.h and
Sachen/Sachen8259.h are attributed only to
rustynes-mappers/src/sachen_discrete.rs, with mmc3_clones.rs and
multicart_discrete.rs mapped to their actual local references. The sites in
crates/rustynes-cpu/src/cpu.rs:877-882,
crates/rustynes-mappers/src/m513_sachen_9602.rs:347-349, and
crates/rustynes-ppu/src/ppu.rs:759-761 require no direct changes; update the
corresponding provenance entries for
crates/rustynes-mappers/src/mmc3_clones.rs:784-785 and
crates/rustynes-mappers/src/multicart_discrete.rs:3805-3806 as part of the table
correction.

In `@docs/ai-emulator-provenance-guardrails.md`:
- Around line 150-156: Update the licensing guidance around “Determine each
derived-from source's exact license” and “The combined work takes the strongest
copyleft” to require removing or rewriting an incompatible code component, or
obtaining compatible permission, before documenting remediation. Make clear that
documentation changes alone do not resolve incompatible code licenses, and only
update the documentation after the underlying code/license issue is resolved.
- Around line 174-179: Update the “Firewall check” guidance to make the
tool-level read-deny or sandbox policy the enforced control for blocking access
to reference-source trees, including sibling, mounted, read-only-mounted, and
renamed paths. Retain the existing git ls-files/grep command only as a
supplemental repository-path check, not as the firewall mechanism.
🪄 Autofix

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: 39edce25-9385-41a8-b20c-b494d8ecd4c8

📥 Commits

Reviewing files that changed from the base of the PR and between d5c0bab and 905fe02.

⛔ Files ignored due to path filters (1)
  • ref-docs/RustyNES_Provenance-Failure-Postmortem.pdf is excluded by !**/*.pdf, !ref-docs/**
📒 Files selected for processing (20)
  • crates/rustynes-apu/src/blip.rs
  • crates/rustynes-cpu/src/cpu.rs
  • crates/rustynes-frontend/src/debugger/source_map.rs
  • crates/rustynes-frontend/src/ntsc_bisqwit.rs
  • crates/rustynes-mappers/src/fds.rs
  • crates/rustynes-mappers/src/kaiser.rs
  • crates/rustynes-mappers/src/lib.rs
  • crates/rustynes-mappers/src/m016_bandai_fcg.rs
  • crates/rustynes-mappers/src/m035_jy_asic.rs
  • crates/rustynes-mappers/src/m176_bmc_fk23c.rs
  • crates/rustynes-mappers/src/m268_bmc_coolboy.rs
  • crates/rustynes-mappers/src/m513_sachen_9602.rs
  • crates/rustynes-mappers/src/mmc3_clones.rs
  • crates/rustynes-mappers/src/multicart_discrete.rs
  • crates/rustynes-mappers/src/ntdec.rs
  • crates/rustynes-mappers/src/sachen_discrete.rs
  • crates/rustynes-mappers/src/unif.rs
  • crates/rustynes-ppu/src/ppu.rs
  • docs/ai-emulator-provenance-guardrails.md
  • docs/provenance-failure-postmortem.md

Comment thread crates/rustynes-cpu/src/cpu.rs
Comment thread docs/ai-emulator-provenance-guardrails.md
Comment thread docs/ai-emulator-provenance-guardrails.md Outdated
doublegate and others added 5 commits August 4, 2026 20:33
… layout

Reflow `ref-docs/RustyNES_Provenance-Failure-Postmortem.pdf` from the
prior dense two-column treatment to a single wide-column, full-page
layout. The two-column measure forced an awkward mid-title column break
and cramped the first page; the evidence timeline table (already a
full-width block) sat inconsistently between the two flowed columns.

Typographic changes (theme CSS, WeasyPrint pipeline unchanged otherwise):
- `.cols` collapses from `columns: 2` (with a column rule) to a plain
  single-column block; body text switches from justified to left-aligned
  (ragged right) for the wider measure.
- Base type 8.75pt -> 10.2pt, line-height 1.4 -> 1.5; page margins
  widened to 1.9/2.1/1.6/2.1cm to hold the single-column measure near a
  comfortable ~80-char line rather than a full 19cm bleed.
- Headings scaled to the new base (h2 11.5 -> 13.5pt, h3 9.6 -> 11pt),
  the evidence table 6.9 -> 8.4pt, code blocks 7.6 -> 8.6pt, and the
  maintainer's NOTE box 8.6 -> 10pt.

Cool blue/teal structure, red-only-for-takeaways emphasis, and the
blue-shaded "MAINTAINER'S STATEMENT" NOTE box are all preserved; the
document grows from 4 to 5 pages, which the maintainer accepted
("regardless of the final page count"). Source markdown
`docs/provenance-failure-postmortem.md` is unchanged.

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

Two presentation tweaks to the closing "Maintainer's Statement":

- Source `docs/provenance-failure-postmortem.md`: remove the surrounding
  quotation marks from the DoubleGate NOTE so the statement reads as a
  first-person remark rather than a quoted block. Wording unchanged.
- Regenerated `ref-docs/RustyNES_Provenance-Failure-Postmortem.pdf`: the
  PDF assembly now wraps everything after "NOTE (from DoubleGate): " in
  an <em>, so the statement body renders italic while the "NOTE
  (from DoubleGate):" label stays upright. This is a PDF-only styling
  step (the assembler splits the note paragraph at the "): " marker and
  re-parents the trailing nodes — including the `~/.claude/` code span —
  under an emphasis element); the Markdown source carries no emphasis
  markup so its own rendering is unaffected.

Layout, theme, and page count (5) are otherwise unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ons and prevention"

In the maintainer's NOTE, change the cross-reference from "#7 'Lessons
and prevention'" to a bold "7. Lessons and prevention" in both surfaces:

- Source `docs/provenance-failure-postmortem.md`: wrap the phrase in
  `**...**` and drop the `#` prefix and single quotes.
- Regenerated `ref-docs/RustyNES_Provenance-Failure-Postmortem.pdf`: the
  phrase is now a <strong> inside the italic NOTE body, so it reads as a
  bold section label (the assembler re-parents it under the note's <em>
  along with the rest of the statement body).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PDF-only styling: within the italic NOTE body, "Fiskbit" is now wrapped
in a <strong class="upright"> so it reads bold and non-italicized while
the rest of the statement stays italic. The assembler splits the note
text run and re-parents the name under an upright strong; the theme adds
`.note-box strong.upright { font-style: normal; }` to cancel the
inherited italic (leaving the bold "7. Lessons and prevention" label,
also a strong, italic as before). Markdown source unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Render `docs/ai-emulator-provenance-guardrails.md` to a styled PDF at
`ref-docs/AI-Emulator-Provenance-Guardrails.pdf`, using the same cool
"Calm" family theme as RustyNES_Provenance-Failure-Postmortem.pdf so the
two provenance documents read as a set.

Pipeline (pandoc gfm -> html5, a small BeautifulSoup title-block/emphasis
pass, WeasyPrint with a dedicated theme CSS):
- Full-width single-column, humanist sans (Fira Sans), blue/teal
  structure; red reserved for the few hardest takeaways ("capability +
  availability + accuracy objective", "C used as if it were A",
  "source physically unavailable to the agent", "Never launder").
- Title block reflecting the doc's own framing (community best-guidance;
  ready-to-ingest ruleset), running header/footer retitled for this doc.
- The document's own structures styled to match: the "in one sentence"
  blockquote becomes a teal TL;DR callout; GitHub task-list checklists
  render as blue-outlined checkboxes (the real <input> hidden, the box
  drawn as an absolutely-positioned gutter marker — reliable in
  WeasyPrint); the paste-ready block keeps the monospace code panel;
  tables get the navy-header/zebra treatment with hyphenated long words.

8 pages. The Markdown source is unchanged; this is a presentation
artifact derived from it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
doublegate and others added 6 commits August 4, 2026 20:51
…ls PDF

The §8 code panel rendered at 8.6pt, so its ~95-char lines wrapped raggedly
inside the box and one bullet's leading "-" was orphaned onto its own line.
Drop the monospace size to 6.9pt (the block's lines now fit the panel
width) and add a hanging indent so any residual continuation line stays
readable under its bullet. The document tightens from 8 to 7 pages;
nothing else changes.

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

Add a `## MOST IMPORTANT RULE — Provenance & license firewall` section at
the top of the AGENTS.md project block (reached via the CLAUDE.md / GEMINI.md
symlinks), positioned before "## What this is" so it is the first substantive
guidance every session loads. It is declared to outrank everything else in the
file.

Motivation: RustyNES is a corrected provenance failure — GPL emulator source
(Mesen2, puNES, FCEUX, GeraNES) was reproduced despite a black-box instruction,
the honest "ported from X" comments were later scrubbed, and the project was
relicensed MIT/Apache -> GPL-3.0-or-later as the derivative work it actually is.
The failure was caught by an outside NESdev reviewer, not by tooling, which is
the empirical basis for the "do not self-certify" clause. The full preventive
ruleset now lives in docs/ai-emulator-provenance-guardrails.md (with a forensic
post-mortem in docs/provenance-failure-postmortem.md); this section is the
always-loaded distillation that binds an agent before it touches any file.

The section encodes the six non-negotiables — the REFERENCE FIREWALL (reference
emulators are black-box oracles whose output may be observed but whose source is
never read or reproduced; the local ref-proj/ clone is removed from disk and
stays gitignored so the source is out of reach by design), IMPLEMENT FROM DOCS,
IF YOU DERIVE SAY SO AND STOP (attribute at the site + originality doc §1 +
NOTICE + SPDX; keep the license GPL-3.0-or-later-compatible), NEVER LAUNDER, NO
OVER-ATTRIBUTION, and DO NOT SELF-CERTIFY — and points at the mechanical
enforcement that backs the prose (the gitignore / dockerignore / markdownlintignore
/ CodeRabbit exclusions, deny.toml, and the per-file SPDX + provenance headers),
on the principle that a rule the tooling enforces beats a rule an agent is merely
asked to follow. Also updates the existing `.markdownlintignore` note to record
that ref-proj/ is now removed from disk but retained in the ignore lists as a
firewall guard rather than as a build convenience.

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

Delete the removed-clone `ref-proj/` prefix from every in-source provenance
citation so each names the upstream project + file directly (attribution
surface #1 of the guardrails: "the upstream project, the specific file/function,
and its license"), rather than a path into a local working copy that no longer
exists. `ref-proj/GeraNES/src/GeraNES/Mappers/Mapper0NN.h` becomes
`GeraNES/src/GeraNES/Mappers/Mapper0NN.h`; `ref-proj/Mesen2/Core/...`,
`ref-proj/TriCNES/Emulator.cs`, `ref-proj/tetanes`, and `ref-proj/fceux/...`
likewise. The one non-path use — m024_vrc6.rs's "a cross-check against the whole
`ref-proj/` field" — is reworded to "the whole field of reference emulators".

This is a pure path/wording normalization: it does not change any derivation
claim. The sites that genuinely document a port keep their verb and license
verbatim — ppu.rs still reads "Ported from TriCNES (`TriCNES/Emulator.cs`, MIT,
commit 9199870)", and m093_sunsoft3r.rs still says its `writePrg` matches "the
designated reference `GeraNES/src/GeraNES/Mappers/Mapper093.h`, whose `writePrg`
opens with `data &= readPrg(addr);`". Nothing is softened, laundered, or
over-attributed; only the dangling local-clone prefix is removed.

Scope: 30 files across rustynes-core (movie_interop), rustynes-frontend (crt +
two debugger panels), rustynes-mappers (28 board modules), and rustynes-ppu.
The changes are comments and doc-comments only — no code tokens move — so the
compiled `#![no_std]` chip stack is byte-identical, the deterministic contract
is untouched, and AccuracyCoin holds 141/141 and nestest stays 0-diff by
construction. Verified: `cargo check --workspace` clean,
`RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps` clean,
`cargo fmt --all --check` clean, and `git grep "ref-proj/" -- crates/**/*.rs`
now returns nothing.

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

The local `ref-proj/` reference-emulator clone (Mesen2, puNES, FCEUX, GeraNES,
TriCNES, tetanes, ...) has been removed from disk. Update the surrounding
documentation and ignore configuration so nothing points a developer — or an
agent — back at reference-emulator source, and so the firewall is stated where
the setup that used to depend on ref-proj/ lived.

- .gitignore: keep the `/ref-proj/` entry but re-annotate it as a *firewall
  guard* — the directory is removed and must never re-enter the working tree,
  because its copyleft source is what made RustyNES a derivative work. The entry
  now cross-links the guardrails doc and the AGENTS.md top rule. (The parallel
  ignores in .dockerignore / .markdownlintignore / .pre-commit-config.yaml /
  .coderabbit.yaml are retained unchanged for the same belt-and-suspenders
  reason.)

- Oracle / trace tooling (docs/tooling/oracle-tooling-setup.md,
  docs/ppu-trace-tooling.md): add a REFERENCE FIREWALL banner and rewrite the
  ref-proj/ paths. These guides build and instrument a reference emulator to
  capture its *output* for cross-diffing — legitimate black-box-oracle use — so
  they now state that any such build must live out-of-tree, outside the agent's
  allowed paths, and be used for output only; the committed golden vectors
  (crates/rustynes-test-harness/golden/) remain the preferred, self-contained
  path that needs no reference source at all.

- Provenance / spec docs: originality-and-provenance.md records that the §1
  derivation table was cross-checked against the sources at the time (the
  since-removed ref-proj/ clone) and stands on its named upstream citations;
  STATUS.md, to-dos/ROADMAP.md, adr/0030, adr/0006, apu-2a03.md,
  hd-pack-zelda-troubleshooting.md, and SALVAGE_MANIFEST.md have their ref-proj/
  citations normalized to upstream (or, where they said "vendored ref-proj/X",
  corrected to "out-of-tree" / "in-repo", since the clone is no longer vendored).

- Discoverability: add a "Provenance & Licensing" section to
  docs/DOCUMENTATION_INDEX.md and a matching group to the mkdocs nav so the
  guardrails, post-mortem, originality record, and ADR 0036 are linked from the
  documentation entry points rather than only from AGENTS.md.

Frozen / historical trees (docs/archive/, to-dos/archive/, to-dos/plans/**,
ref-docs/, .github/release-notes/, CHANGELOG-FULL.md) deliberately keep their
ref-proj/ mentions as immutable record. No behavior changes; markdownlint clean.

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

Surface the provenance guardrails on the two developer-facing entry points.

- README.md: add a "Reference firewall (so it does not recur)" paragraph to the
  License section, immediately after the existing GPLv3-derivation and
  AI-assistance disclosures. It names the forensic post-mortem and the
  console-agnostic guardrails ruleset (with the themed PDFs in ref-docs/),
  states that it is the project's top development rule ingested into AGENTS.md,
  and summarizes the firewall: reference emulators are black-box oracles whose
  output may be observed but whose source is never read; the ref-proj/ clone is
  removed and gitignored so that source is out of reach; hardware behavior is
  implemented from documentation and test ROMs; genuine derivation is attributed
  and license-checked, never laundered. Notes the guardrails are shared as
  community best-guidance for other AI-assisted emulator projects.

- CHANGELOG.md [Unreleased]: add a "Provenance guardrails + reference firewall"
  block recording the new guardrails doc + post-mortem + PDFs, the ingestion
  into AGENTS.md and the memory bank, the ref-proj/ removal and the retained
  firewall ignores, the comments-only normalization of in-source citations to
  upstream paths (deterministic core byte-identical), the out-of-tree oracle
  posture in the tooling docs, and the new documentation-index / mkdocs-nav
  entries.

Documentation only. markdownlint clean.

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

A CodeRabbit review of #346 flagged a per-site ↔ central-table inconsistency in
the provenance record. A full audit of every `derived from Mesen2's \`X\`` comment
against its `docs/originality-and-provenance.md` §1 row confirmed six files whose
in-source citations name an upstream Mesen2 header that the central table row
omitted. Each such header is a shared chip/transform that a sibling file already
lists in its own row, so the table under-reported which files derive from it:

- `kaiser.rs` (mapper-253 IRQ board, line 614) derives from `Waixing/Mapper253.h`.
- `m035_jy_asic.rs` (`invert_prg_bits`, line 315) derives from `InvertPrgBits`.
- `m176_bmc_fk23c.rs` (CoolBoy banking, line 554) derives from `Mmc3Variants/MMC3_Coolboy.h`.
- `m513_sachen_9602.rs` (TxcChip accumulator, line 349) derives from `Txc/TxcChip.h`.
- `mmc3_clones.rs` (Sachen 8259A/B/C, mappers 138/139/141, line 784) derives from `Sachen/Sachen8259.h`.
- `ntdec.rs` (BMC-11160, line 1262) derives from `Txc/Bmc11160.h`.

Add each missing upstream header to the corresponding table row so the central
derivation record is fully consistent with the per-site attributions, per the
provenance guardrails' "provenance-comment ↔ table consistency" rule. This is an
alignment, not new attribution: each derivation is already asserted verbatim in
the source comment; the table now records what the code already documents (a
correction toward completeness, not over-attribution). No license changes — all
six sources are Mesen2 (GPL-3.0-or-later), already the project's license.
Documentation only; markdownlint clean.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/rustynes-frontend/src/debugger/input_miniatures_panel.rs (1)

132-147: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Enable native multi-viewport support before claiming OS-window detachment.

super::detachable_window uses show_viewport_immediate, but crates/rustynes-frontend/src/debugger/mod.rs:285-367 states that the current egui_winit integration is single-viewport. The detached panel therefore renders embedded in the main window instead of opening a native OS window.

Enable multi-viewport handling in the frontend render loop, or update the release behavior and documentation to describe embedded detachment.

Also applies to: 169-170

🤖 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 `@crates/rustynes-frontend/src/debugger/input_miniatures_panel.rs` around lines
132 - 147, Enable native multi-viewport support in the frontend render loop used
by super::detachable_window before relying on show_viewport_immediate for
detached panels. Update the egui_winit integration and viewport event/render
handling so “Input Display” opens as a native OS window, and apply the same
behavior to the other detachable panel call sites. If multi-viewport support
cannot be enabled, change detachable_window’s release behavior and documentation
to describe embedded detachment instead.
🤖 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 @.gitignore:
- Around line 191-194: Update the .gitignore comment for /ref-proj/ to remove
the blanket claim that all referenced emulator sources are copyleft, stating
instead that project licenses are specific (including TriCNES as MIT) or
omitting the license claim. Preserve the existing /ref-proj/ ignore rule.

In `@docs/DOCUMENTATION_INDEX.md`:
- Around line 48-61: Remove the literal ref-proj/ path from the provenance
documentation entry in DOCUMENTATION_INDEX.md, while preserving the explanation
that the reference clone was removed and firewall-gitignored. Do not add that
repository path elsewhere in the documentation index.

In `@docs/originality-and-provenance.md`:
- Around line 47-51: Remove the published ref-proj/ repository path from the
provenance description in docs/originality-and-provenance.md lines 47-51,
describing the removed reference-emulator clone without reproducing its path.
Apply the same wording change to the documentation index description in
docs/DOCUMENTATION_INDEX.md lines 48-61; both sites require direct updates.

In `@docs/tooling/oracle-tooling-setup.md`:
- Around line 3-21: Resolve the contradictory reference-source policy in the
documentation: retain the firewall rule that reference emulators are black-box
oracles, remove or relocate the vendored TriCNES source under the golden
artifacts, and update the preferred harness guidance to reference only outputs
and golden vectors. Ensure the related sections consistently prohibit
in-repository reference source.

---

Outside diff comments:
In `@crates/rustynes-frontend/src/debugger/input_miniatures_panel.rs`:
- Around line 132-147: Enable native multi-viewport support in the frontend
render loop used by super::detachable_window before relying on
show_viewport_immediate for detached panels. Update the egui_winit integration
and viewport event/render handling so “Input Display” opens as a native OS
window, and apply the same behavior to the other detachable panel call sites. If
multi-viewport support cannot be enabled, change detachable_window’s release
behavior and documentation to describe embedded detachment instead.
🪄 Autofix

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: 52fa777b-8539-4f3e-bd22-3771d1457291

📥 Commits

Reviewing files that changed from the base of the PR and between 905fe02 and 5a202e7.

⛔ Files ignored due to path filters (2)
  • ref-docs/AI-Emulator-Provenance-Guardrails.pdf is excluded by !**/*.pdf, !ref-docs/**
  • ref-docs/RustyNES_Provenance-Failure-Postmortem.pdf is excluded by !**/*.pdf, !ref-docs/**
📒 Files selected for processing (48)
  • .gitignore
  • AGENTS.md
  • CHANGELOG.md
  • README.md
  • crates/rustynes-core/src/movie_interop.rs
  • crates/rustynes-frontend/src/crt.rs
  • crates/rustynes-frontend/src/debugger/hd_pixel_panel.rs
  • crates/rustynes-frontend/src/debugger/input_miniatures_panel.rs
  • crates/rustynes-mappers/src/homebrew_boards.rs
  • crates/rustynes-mappers/src/jaleco_discrete.rs
  • crates/rustynes-mappers/src/m024_vrc6.rs
  • crates/rustynes-mappers/src/m035_jy_asic.rs
  • crates/rustynes-mappers/src/m038_bitcorp38.rs
  • crates/rustynes-mappers/src/m039_subor39.rs
  • crates/rustynes-mappers/src/m041_caltron41.rs
  • crates/rustynes-mappers/src/m079_ave_nina03_06.rs
  • crates/rustynes-mappers/src/m093_sunsoft3r.rs
  • crates/rustynes-mappers/src/m096_bandai96.rs
  • crates/rustynes-mappers/src/m107_magic_dragon107.rs
  • crates/rustynes-mappers/src/m113_ave_nina006.rs
  • crates/rustynes-mappers/src/m156_daou156.rs
  • crates/rustynes-mappers/src/m180_nichibutsu180.rs
  • crates/rustynes-mappers/src/m185_cnrom185.rs
  • crates/rustynes-mappers/src/m232_camerica_bf9096.rs
  • crates/rustynes-mappers/src/m240_cne_multicart.rs
  • crates/rustynes-mappers/src/m241_bxrom241.rs
  • crates/rustynes-mappers/src/m244_cne_decathlon.rs
  • crates/rustynes-mappers/src/m246_fong_shen_bang246.rs
  • crates/rustynes-mappers/src/m250_nitra250.rs
  • crates/rustynes-mappers/src/multicart_discrete.rs
  • crates/rustynes-mappers/src/ntdec.rs
  • crates/rustynes-mappers/src/sachen_8259.rs
  • crates/rustynes-mappers/src/sachen_discrete.rs
  • crates/rustynes-ppu/src/ppu.rs
  • docs/DOCUMENTATION_INDEX.md
  • docs/SALVAGE_MANIFEST.md
  • docs/STATUS.md
  • docs/adr/0006-vrc7-audio-landed.md
  • docs/adr/0030-accuracycoin-ale-read-hybrid-addresses-octal-latch.md
  • docs/ai-emulator-provenance-guardrails.md
  • docs/apu-2a03.md
  • docs/hd-pack-zelda-troubleshooting.md
  • docs/originality-and-provenance.md
  • docs/ppu-trace-tooling.md
  • docs/provenance-failure-postmortem.md
  • docs/tooling/oracle-tooling-setup.md
  • mkdocs.yml
  • to-dos/ROADMAP.md

Comment thread .gitignore Outdated
Comment thread docs/DOCUMENTATION_INDEX.md
Comment thread docs/originality-and-provenance.md
Comment thread docs/tooling/oracle-tooling-setup.md Outdated
@doublegate
doublegate enabled auto-merge (squash) August 5, 2026 02:16
…nore docs

Address four follow-on CodeRabbit findings on #346:

- .gitignore: the ref-proj/ guard comment claimed the referenced emulators'
  source is uniformly "copyleft". That over-generalized — Mesen2 / puNES / FCEUX
  / GeraNES are GPL, but TriCNES is MIT. Reword to state the licenses are
  project-specific (copyleft for the four GPL oracles; MIT for TriCNES) and note
  that the MIT TriCNES source is instead deliberately vendored, with attribution,
  under crates/rustynes-test-harness/golden/tricnes/. The /ref-proj/ ignore rule
  itself is unchanged.

- docs/tooling/oracle-tooling-setup.md: resolve a genuine contradiction. The two
  firewall notes said "any Mesen2 / TriCNES build must live out of tree", but the
  same page (§2a) vendors the complete MIT TriCNES source in-repo at
  golden/tricnes/tricnes-full-src/ and calls the in-tree harness the preferred
  path. Scope the out-of-tree / never-reproduce rule to the copyleft references
  (Mesen2, puNES, FCEUX, GeraNES) and state TriCNES explicitly as the MIT
  exception whose in-repo vendoring is license-compatible and not a firewall
  violation. The committed golden vectors remain the preferred, no-live-emulator
  path.

- docs/originality-and-provenance.md and docs/DOCUMENTATION_INDEX.md: describe the
  removed reference-emulator clone without reproducing its literal repository path
  in these provenance/index prose additions (the path stays authoritatively named
  where it is load-bearing — the /ref-proj/ ignore rule and the AGENTS.md rule).

Documentation only; markdownlint clean. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@doublegate
doublegate disabled auto-merge August 5, 2026 02:30
doublegate and others added 2 commits August 4, 2026 22:39
…fication

`bk2_interop::parse_log_key` collected every `#`-separated group of the BizHawk
`LogKey` header into a `Vec<&str>` and then read only `groups[1]` (P1) and
`groups[2]` (P2). A hostile `.bk2` whose `LogKey` is padded with a large run of
`#` delimiters therefore allocated one `&str` slot (~16 bytes on 64-bit) per
empty group — an unbounded, ~16x-of-input allocation on an untrusted import path,
the same DoS class the v2.2.0 `Movie::deserialize` fuzzing already closed
elsewhere.

Read the three groups we actually consume (console, P1, P2) directly from the
`split('#')` iterator via `next()` instead of collecting. `split` still yields
empty groups, so `next()` preserves the empty console slot (`##P1…`) and keeps
P1/P2 from shifting left into it — the behavior is byte-identical for every valid
movie, only the unbounded intermediate allocation is removed. The parse now
touches at most three groups regardless of how many `#` the input contains.

Adds `log_key_bounded_against_pathological_group_padding`, which imports a movie
whose `LogKey` carries 100k trailing `#` delimiters and asserts P1/P2 still map
correctly (the trailing groups are ignored), as the standing regression guard.
This is the `.bk2` *import* path only; the deterministic chip stack and every
golden vector are untouched (AccuracyCoin 141/141 unaffected).

Reported by CodeRabbit as an outside-diff-range finding on #346.

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

Three corrections to the [2.2.9] release notes (which release-auto.yml publishes
verbatim, so accuracy and completeness there matter):

- **Honesty fix (CodeRabbit outside-diff-range finding).** The intro note claimed
  detached tool windows use "egui multi-viewport (real OS windows)", which
  contradicted both the detailed "Fixed" entry and AGENTS.md: the frontend is a
  single-viewport `egui_winit` integration, so `show_viewport_immediate` renders a
  detached panel *embedded in the main window*, not as a separate OS window, and
  the Windows-10 "trapped window" report is therefore not yet fully resolved.
  Reword the note to state the embedded scope honestly and point at the v2.3.0
  multi-viewport follow-up, matching the rest of the section.

- **Fold [Unreleased] into [2.2.9].** The provenance-guardrails + reference-firewall
  work (guardrails doc + post-mortem + PDFs, ingestion into AGENTS.md and memory,
  the ref-proj/ removal and citation normalization, the §1 derivation-table audit,
  and the MIT-TriCNES vendoring exception) all ship in v2.2.9, so it belongs in
  the v2.2.9 notes rather than a separate [Unreleased] section the release body
  would omit. Recorded as a new "Added — Provenance & license firewall" subsection.

- **Log the .bk2 import hardening** (the `LogKey` allocation-amplification bound)
  in the same subsection.

Documentation only; markdownlint + fmt clean.

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

Copy link
Copy Markdown
Owner Author

Adjudication of the "Outside diff range" findings

These aren't resolvable inline threads, so recording the disposition here.

1. crates/rustynes-core/src/bk2_interop.rs — unbounded LogKey group allocation → FIXED (c96dba37).
Correct and in-scope: parse_log_key collected every #-group but read only P1/P2, so a #-padded .bk2 amplified to ~16 bytes/delimiter on an untrusted import path (the same DoS class the v2.2.0 Movie::deserialize fuzzing closed). Now reads only the three consumed groups (console/P1/P2) straight from the split('#') iterator — byte-identical for valid movies, no unbounded intermediate. Added log_key_bounded_against_pathological_group_padding (100k trailing #) as the regression guard.

2/3. CHANGELOG.md v2.3.0 references → declined (intentional, pre-existing).
The v2.3.0 mentions are forward-planning context for the v2.2.6 → v2.3.0 remediation line (and the deferred TriCNES-rework/PPU-left-edge work), explicitly framed as planned, not released. They are pre-existing maintainer-authored release history; my additions introduced none. The guideline forbids claiming a later release shipped, which the text does not do.

4. input_miniatures_panel.rs multi-viewport detachment → addressed by documentation (c96dba37).
The panels genuinely embed (single-viewport egui_winit) rather than opening OS windows — enabling multi-viewport is a render-loop rewire explicitly deferred to v2.3.0. Your "or document embedded detachment" option was already met in the detailed Fixed entry and AGENTS.md, but the [2.2.9] intro note still overclaimed "real OS windows"; that note is now corrected to state the embedded scope honestly and point at the v2.3.0 follow-up.

@doublegate
doublegate enabled auto-merge (squash) August 5, 2026 02:42
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR updates RustyNES to v2.2.9 "Studio II", wiring TAStudio piano-roll input edits to trigger an emulator re-seek, parsing dynamic LogKey column headers in .bk2 movie files, introducing a detachable_window UI abstraction for debugger panels, relicensing the codebase to GPL-3.0-or-later, and updating accompanying provenance documentation.

Blocking issues

None found.

Suggestions

  • STATUS.md:L4826: STATUS.md states that tool windows "detach into real OS windows (fixing the Windows-10 trapped-window report)". This conflicts with AGENTS.md:L72, frontend.md:L1745, and the code in mod.rs:L3144-L3150, which document that show_viewport_immediate currently renders panels embedded within the main window because multi-viewport render loop integration is deferred. Update STATUS.md to accurately state the current embedded scope.
  • bk2_interop.rs:L1832-L1834: parse_log_key unconditionally discards groups.next() as the console group. If a .bk2 input log contains a LogKey: header lacking a leading # or console section (for example, LogKey:P1 Up|P1 Down...), groups.next() will consume the P1 group as console data and drop it, causing input fields to shift. Verify group headers or check for leading delimiters before skipping the first element.

Nitpicks

  • mod.rs:L3153: Unicode symbol \u{29c9} () is used in UI button strings (\u{29c9} Reattach and \u{29c9} Detach). Ensure non-ASCII symbols in UI labels adhere to project conventions against decorative glyphs/emojis in code.

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

@doublegate
doublegate merged commit bcc9c76 into main Aug 5, 2026
28 checks passed
@doublegate
doublegate deleted the feat/v2.2.9-studio-ii-tas-ux branch August 5, 2026 02:57
doublegate added a commit that referenced this pull request Aug 5, 2026
… tool windows; GPL-3.0-or-later relicense (#346)

Two independent bodies of work land together in v2.2.9: a frontend
quality-of-life pass ("Studio II") and the licensing/provenance remediation
prompted by NESdev-community review. Both are confined to the frontend and the
docs/licensing surface — the deterministic `#![no_std]` chip stack, the
save-state / TAS / netplay formats, and every golden vector are byte-identical,
so AccuracyCoin holds 141/141 (100.00%) and nestest is 0-diff by construction.

Frontend — "Studio II"
----------------------
TAStudio piano-roll edits now drive the emulator. The panel path
(`handle_tas_requests`) previously mutated only the editor's `input_log` on a
`SetInput` and never re-derived the running `Nes`, so a cell edit looked
disconnected from emulation (the "TAStudio inputs are not connected up" report).
It now tracks an `input_dirty` flag across a batch and does a single
deterministic re-seek to the cursor afterward, exactly as the scripting path
(`apply_tas_commands`) already did; `InsertFrame` / `DeleteFrame` / `StampMacro`
also mark dirty, while `Seek` / `CreateBranch` / `LoadBranch` reseat the `Nes`
themselves.

`.bk2` movie import honors the movie's column order. The parser ignored the
`LogKey:` header and mapped pad columns by a fixed `U D L R S s B A` position, so
a BizHawk movie authored with a different order or extra columns mapped every
button to the wrong bit ("`.bk2` did not play back"). `parse_log_key` now reads
the per-port column order from the `LogKey` (`#` groups, `|` columns), maps each
column by its button name (ignoring the `Pn ` prefix), and falls back to the
standard order when a group is truncated or exotic; `parse_pad` maps by that
column list and tolerates a group longer than the modeled columns (extra buttons
such as a microphone are ignored). A new test covers a non-standard order plus an
extra column.

Tool windows gain a detach / pop-out affordance via a shared `detachable_window`
helper wired across 18 panels, and per-panel window geometry (first-open
position / size / resizability) is preserved through it. Honest scope caveat:
the frontend is a single-viewport `egui_winit` integration, so
`show_viewport_immediate` currently renders a detached panel *embedded* in the
main window rather than as a separate OS window — this does not yet fully resolve
the Windows-10 "trapped window" report; true OS-window detach needs
multi-viewport render-loop wiring and is tracked as a v2.3.0 follow-up. All
detach code is native-only; wasm stays docked.

Licensing & provenance — relicense to GPL-3.0-or-later (ADR 0036)
----------------------------------------------------------------
A NESdev-community review established that RustyNES is a derivative work of
GPL-licensed emulators: it incorporates code derived from Mesen2
(GPL-3.0-or-later) and, for several mappers and the FDS drive model, from puNES,
FCEUX, and Nestopia UE (all GPL-2.0-or-later). The v2.2.5 "no GPL source
incorporated" / MIT-OR-Apache-2.0 position was therefore wrong and is withdrawn:
`LICENSE` becomes GPL-3.0-or-later and `deny.toml` is updated to match. Every
genuinely-derived source file carries a corrected SPDX identifier plus a
per-file provenance header naming the upstream file/function it was ported or
closely modeled from, and the earlier "laundered" per-function comments at those
ported sites are restored to state the real derivation. `NOTICE` is rewritten to
separate derived code (why the project is GPL) from behavioral-oracle use (no
code incorporated), and `docs/originality-and-provenance.md` leads with the
file-by-file derivation record.

Documentation
-------------
Adds the provenance-failure post-mortem (`docs/provenance-failure-postmortem.md`)
and the console-agnostic, ingestible provenance/license guardrails for
AI-assisted emulator development (`docs/ai-emulator-provenance-guardrails.md`),
each with a themed PDF in `ref-docs/`; folds in the CodeRabbit / Copilot #346
review items; and bumps the README version badge to v2.2.9.

Verification
------------
Frontend- and docs-only, so the emulation core is untouched: AccuracyCoin
141/141, nestest 0-diff, blargg/kevtris and the 60-ROM oracle unchanged. The
tree passes `cargo fmt`, `clippy -D warnings` (workspace + feature combos),
`rustdoc -D warnings`, markdownlint, and the `no_std` cross-compile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants