You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Owner directive, 2026-08-03, triggered by #74: "We need to stop cheating in our testing... If you are not playing actual game states that have been reached through actual game choices, you are just wasting time pretending." This issue tracks the program until the whole test system plays for real.
The rule
A test or mint script may inject controller input and read emulated memory to assert things. It may never write emulated game state. Determinism comes from fixed, honestly-minted fixtures plus frame-exact input (TAS-style), never from rigging. Fixture honesty is transitive: a savestate or anchor minted by a script that poked state is contaminated, and so is everything derived from it.
What the census found (2026-08-03, three parallel audits)
796 state-write call sites across the 82 suite tests; 233 of 327 .lua files under tools/tests write state.
2 of ~110 fixtures are honest (battle_doorstep, first_battle — cold power-on, pure input). Everything else is contaminated, chiefly by the kill-bit idiom ($3EEC |= $80) that lives inside seven lib/ot6_field.lua navigation helpers and fires on every random encounter, plus per-leg pins/clamps/pokes. Every tracked battery anchor was cut through a ZMENUSTATE poke the README itself forbids, and carries harness-authored witness bytes.
Every boss from the Whelk to Number 128 has been "won" by kill-bit in the mint chain. No automated play has ever beaten a boss honestly past the two existence proofs below.
The cost of this showed up as v0.6 Phantom Train #74: gen_sabin_train.lua pins HP/MP, freezes everyone but Sabin, and clamps the Phantom Train to 1 HP — so a break loop that real players cannot complete sailed through every gate since v0.6.
Two in-tree proofs that honest play works: battle_vargas.lua drives Blitzes by real pad edges, and gen_sabin_magitek.lua wins its battles honestly because kill-bitting them softlocks. The honest rewrite is engineering, not research.
The plan (landing order)
Static checker ratchet (tools/check_state_writes.py, in the make test checker family): every write idiom in tools/tests/**/*.lua fails the build unless it's in a checked-in burn-down waiver list that may only shrink. New cheats become impossible to land immediately. (In progress.)
Cheap principled conversions: the four save-menu ZMENUSTATE pokes → real pad drives (the repo already has the template and its own rule requiring this). (In progress.)
Honest library mode: replace the kill-bit in the nav helpers with real battle clearing (tap-A / flee via the engine's own run mechanic), replace H.loadState's codex SRAM wipe with pre-boot battery injection through the existing anchor path.
Runtime gate: compose.py prepends a prologue that removes emu.write*/setState/cheats/reset from every composed script before user code runs; H.loadState keeps the one confined savestate-load handle. Static + runtime = defense in depth.
Provenance: mint stamps additionally bind the artifact hash and the ancestor's stamp hash, and carry a gate-contract version, so make test can verify the whole chain was minted input-only, transitively. Landing the version string deliberately invalidates every existing stamp.
Root-first honest re-mint of the entire frontier (the ninja graph already orders it and auto-invalidates descendants). Suite tests convert wave by wave to honest fixtures + real drives; the burn-down list shrinks to zero.
Known hard legs (each gets its own resolution, none get a rig)
Banon river/hideout legs: any death is Game Over — needs real survival play.
Shadow's 1/16 post-battle leave roll on the Sabin legs — TAS-style timing, not the $1DD2 pin.
The Gau Dried-Meat feed: a probe measured it undrivable even on vanilla — either the probe missed the human-possible input or there is a real game-side question. Investigate before touching game code.
Number 128 with solo Locke at 151 HP: the upstream party menu choice needs revisiting — honest play may simply lose, which would itself be a finding.
The Zozo bridge "hanging" encounter is un-settleable and currently un-mintable without suppression — likely a product bug to fix first.
The banquet's 24 fights inside a 14400-frame timer: feasibility open even with cheats; honest play may force retuning.
The Phantom Train itself (v0.6 Phantom Train #74): the honest mint must beat it with a real strategy; at current tuning that is a razor-thin line, so the fix for v0.6 Phantom Train #74 and the honest re-mint of that leg land together.
Policy note on mechanism tests
A small quarantined class injects inputs the game can only produce rarely or never on cue (deliberate VRAM corruption for the font-restore path, the 1/65536 zero-checksum save, legacy-format codex migration). These are unit tests of mechanisms, not claims about gameplay; they stay, individually waived and loudly labeled, and may never produce fixtures. Everything that claims to test actual play converts, no exceptions.
(Claude here:)
Owner directive, 2026-08-03, triggered by #74: "We need to stop cheating in our testing... If you are not playing actual game states that have been reached through actual game choices, you are just wasting time pretending." This issue tracks the program until the whole test system plays for real.
The rule
A test or mint script may inject controller input and read emulated memory to assert things. It may never write emulated game state. Determinism comes from fixed, honestly-minted fixtures plus frame-exact input (TAS-style), never from rigging. Fixture honesty is transitive: a savestate or anchor minted by a script that poked state is contaminated, and so is everything derived from it.
What the census found (2026-08-03, three parallel audits)
.luafiles undertools/testswrite state.battle_doorstep,first_battle— cold power-on, pure input). Everything else is contaminated, chiefly by the kill-bit idiom ($3EEC |= $80) that lives inside sevenlib/ot6_field.luanavigation helpers and fires on every random encounter, plus per-leg pins/clamps/pokes. Every tracked battery anchor was cut through aZMENUSTATEpoke the README itself forbids, and carries harness-authored witness bytes.gen_sabin_train.luapins HP/MP, freezes everyone but Sabin, and clamps the Phantom Train to 1 HP — so a break loop that real players cannot complete sailed through every gate since v0.6.battle_vargas.luadrives Blitzes by real pad edges, andgen_sabin_magitek.luawins its battles honestly because kill-bitting them softlocks. The honest rewrite is engineering, not research.The plan (landing order)
tools/check_state_writes.py, in themake testchecker family): every write idiom intools/tests/**/*.luafails the build unless it's in a checked-in burn-down waiver list that may only shrink. New cheats become impossible to land immediately. (In progress.)ZMENUSTATEpokes → real pad drives (the repo already has the template and its own rule requiring this). (In progress.)H.loadState's codex SRAM wipe with pre-boot battery injection through the existing anchor path.compose.pyprepends a prologue that removesemu.write*/setState/cheats/reset from every composed script before user code runs;H.loadStatekeeps the one confined savestate-load handle. Static + runtime = defense in depth.make testcan verify the whole chain was minted input-only, transitively. Landing the version string deliberately invalidates every existing stamp.Known hard legs (each gets its own resolution, none get a rig)
$1DD2pin.Policy note on mechanism tests
A small quarantined class injects inputs the game can only produce rarely or never on cue (deliberate VRAM corruption for the font-restore path, the 1/65536 zero-checksum save, legacy-format codex migration). These are unit tests of mechanisms, not claims about gameplay; they stay, individually waived and loudly labeled, and may never produce fixtures. Everything that claims to test actual play converts, no exceptions.