Grading and fairness audit — September 16, 2026

Revision 0.6.2 adds native command and recovery fixes to the earlier 0.6.1 audit. Victory remains a binary result derived from the protected native escape transition and actual carried Silmarils. Terminal text, player names, model claims, and public client files never award a win. Native failures invalidate evidence; they are not silently admitted as losses.

Revision 0.6.3 additionally repairs terrain-label clipping and stale text found during manual public play. It changes no scoring or gameplay rules. The original game and the repaired 375-frame replays have identical trusted state and observations; only the two sunlight displays differ. Fresh checks are in .private/validation-20260916-play/.

Native command audit (0.6.2)

Longer mixed-command sequences exposed bugs that short movement tests missed. All native repairs are checked patches against the pinned upstream source, and all test-only setup remains absent from the production engine.

Defect Reproduction Correction
Smithing menu indexes 08a, 0a8a, and 0aaad8a after birth read beyond the main, base-item, and Numbers menu arrays. Convert one-based highlights to zero-based indexes in all three menus.
Uninitialized smithing subtype An invalid subtype letter was checked against the number of item categories. It read an uninitialized sval, sometimes constructing a nonexistent item; sanitizer and production replays displayed different garbage values. Bound subtype selection by the actual subtype count. Regressions exercise all 17 categories and compare complete native replays.
Directional throwing The game treated an endpoint 99 cells away as a monster location, reading outside the map. Inspect a monster only for a valid, occupied target. Keep directional trajectory calculation in the native path routine.
Vertical aiming Archery and throwing indexed direction arrays with the negative codes for < and >. Handle those codes before array access, preserving the native behavior of dropping the projectile at the player's feet.
Archery at the player's location A location target containing the player indexed mon_list[-1]. Require a positive monster index before checking mercy.
Refused equipped-item throw Fear or declining the mercy prompt cancelled the throw after unequipping its item. Defer taking off equipment until those checks accept the throw. Tests verify inventory and turn state remain unchanged.
Throwing through an empty target The spatial-target check examined the player's cell, so the thrown item stopped at an empty aimed square despite the displayed trajectory continuing through it. Check the target cell. A public targeting sequence verifies the item lands before the wall farther along that trajectory.
Empty artefact knowledge Opening the artefact browser before discovering an artefact indexed group -1. Report the empty collection and return safely.
Scrolled knowledge list bounds Code review found that group, artefact, monster and object loops tested an unscrolled index before reading a scrolled one. The guard did not protect the actual read. Test the same index that is displayed; exercise group and item scrolling with accumulated discovery. This is bounds hardening, separate from the reproduced empty-artefact crash.
Truncated HTTP response http.client.IncompleteRead escaped the client's retry handler after an input might already have executed. Retry the identical durable request ID and retain explicit retry recovery.
Interrupted receipt storage A failed receipt write left an active request, but the service still advertised healthy. Report unhealthy until journal recovery completes; verify restart returns the original request without starting another game.

The initial 32-sequence fuzz pass failed in 14 cases: eight smithing array errors and six throwing map errors. After those repairs, a 64-sequence pass found the subtype, Numbers-menu and empty-artefact defects. The retained failing inputs, sanitizer traces and regression outputs are under .private/validation-20260916-deep/. scripts/fuzz_native.py now provides this repeatable check, validates every public/trusted frame, and compares every frame with the production engine or corresponding fixture build.

The final build passed 382 tests, 139,923 fuzzed native frames with exact replay across 128 sequences, the existing 6,435-frame native suite, container recovery and scoring integration. A 17-command public CLI check replayed all 65 native frames and found no projection differences. Evidence hashes and the prepared 0.6.2 plan checks are recorded in .private/validation-20260916-deep/{production,prepared-preflight}.json.

The screen oracle also needed a correction: native z-term sends black glyphs through its wipe hook. The checker now expects a blank at those cells, with a specific white-to-black redraw test. This changes the test expectation, not production rendering. Smithing previews retain upstream's cosmetic RNG draws; the regressions require unchanged character, inventory and turn state rather than incorrectly treating those native draws as a state mutation bug.

Earlier grading and fairness repairs (0.6.1)

Defect Reproduction Correction
Rage leaked concealed monsters Changing a hidden monster's race changed its structured name while the terminal stayed identical. The monster retained its native ml flag, but Rage hid its cell. Apply the native Rage visibility restriction before emitting a monster. Keep visible hostile human @ glyphs distinct from the player.
Listen redraws rerolled perception and advanced gameplay randomness Six update_mon calls in one player turn consumed 24 gameplay RNG draws; several seeds alternated between detected and undetected. Upstream temporarily seeds and restores its RNG for these checks, but the adapter had replaced that RNG. Run native Listen checks inside a separate keyed stream, reset per check and indexed by player turn. Preserve uniform bounded draws, stable perception within the turn, and isolation from combat randomness.
Fatal falls understated depth A real false-floor trap killed the character after moving its native depth from 50 to 100 feet, before the next dungeon loop updated max_depth. Both result and progress reported 50 feet. Use the greater of native current and maximum depth, with the same helper in result and progress reconstruction. Messages do not determine depth.
Recovery ignored the remaining deadline during native reads A pending input replayed with only 0.25 seconds remaining still used the default 60-second response timeout, then failed service initialization. Share deadline-bounded native reads across startup, actions and recovery. Expiration records a time limit; a stall before the deadline remains an infrastructure error.

The failing regressions before repair are retained in .private/validation-20260916-audit/{fairness-before,grading-before,recovery-before}.log. The complete validation record is linked from launch readiness.

Grading checks

The new surface fixtures generate the native Gates level, place the character at its exit with controlled inventory, and send ordinary movement through the command loop, service, journal, audit and final scoring snapshot. They cover:

Existing checks also cover death while carrying a jewel, reaching the throne room without escaping, empty quest stacks, ordinary light sources, nonstandard rules, stale/forged requests, modified summaries and journals, and authenticated scoring. The scorer freezes one independently reconstructed snapshot for every metric. Depth and acquisition/ascent diagnostics do not replace victory.

Listen tests include high- and low-perception controls so disabling the ability cannot satisfy the RNG regression. Rage uses paired hidden-state observations. Native sanitizer and deterministic replay checks include the new fixtures.

These are adversarial and native integration tests, not a proof over every possible C game state. A naturally completed expert quest would still add useful coverage. No model experiment was run for this audit. Old runs and prepared snapshots retain their original implementation; new experiments must use 0.7.0.

The 0.7.0 interface upgrade preserves programmatic grading. Native prompt types and item choices expose only native input context and already rendered descriptions. Guards and acknowledgment send ordinary keys with the same durable receipts and deadlines. The existing adversarial grading checks still pass. All 393 unique tests, native/hidden-state checks and real-container integration/recovery checks passed. Replaying the original 375-frame session (374 keys plus the initial frame) on both new native builds preserved trusted gameplay state and native messages throughout.