# Workflow — start here

## Current public website release — 2026-11-04-r2

Exact already-qualified ROM `89734acf17e7412e4b0aef71039cdcd4737231ba2fa801ccaece72f6eb794355` (CRC32 `2C608F83`),472 native/test sources, source-set `58ff79db84a60e54628e7ac96cbf450f226b3d7984de1af8790f7275da4a772d`.128/128 mandatory groups plus13 supplemental groups pass, with the initial127/128 capture-selector failure and complete18-child affected requalification disclosed.768 footer contexts and real opened H32/H40 pictures pass; grey fields, calibrated Edmonton94→99 OVR, replay continuation and player-centre blue-line penalty rules are included.

Website-only publication: no ROM edits, new test matrix, local/Bodin installation, save reset or user RetroArch operation. Downloads/remaps/progress are preserved. The latest-BIN button is centred first;101-word English introduction precedes the retained4:57 film, accurately labelled as a2026-11-03-r2 recording. Manual controller/live-multiplayer acceptance remains. [Current release, rules, source and QA](H32_FOOTER_RELEASE_20261104_R2.md). Earlier dated sections below are history; the former Nov5 wait was explicitly revoked and the full qualification is complete.


## Private player blue-line penalty rule —2026-11-04; full gate NOV5 ONLY

User confirmed shooter position (native88 body centre) activates first-crossing
latch; actual retreat then ends as native miss, never a steering clamp. Free
pre-line and purely lateral post-line movement; no new puck retreat/still rule.
Private Beta3 SHA0c9763eef54cc35812bbbb95b6fefb6e0dcfbea2fc86de1b9641f3515b62c4f6.
Targeted64 boundary/ABI,3 resets,5 native routes,6 controller-result variants,
2 complete autonomousCPU attempts, retouch/ordinary controls and exact repeat/
legacy rollback pass.37 native and6 browser pictures opened. See private handoff
penalty-blue-line-20261104/REPORT.md. Revised shared whole-group oracle/input
cases syntax checked ONLY. Full matrix expressly deferred until2026-11-05;
no official release/public-image promotion or old128-pass transplant today.


## User feedback fixes —2026-11-04; FULL MATRIX DEFERRED TO NOV5

- [x] Remove flat light/dark name backings with actual patterned font masks;
 native glyph/frame RGB checks and opened UI pictures, separate test2 preview.
- [x] Restore original team-OVR scale: EDM94 unchanged,99 after copied user trades;
 native seven-case/whole-SRAM controls and real94/99 Matchups images.
- [x] Reproduce automatic replay freeze; preserve alternate native9CDC entry;
 targeted clean/post-LineEditor replay→active clock and manual return checks.
- [x] Archive/verify then remove about380MB inactive /tmp data under NAS backups.
- [ ] **2026-11-05, at jdc's explicit request:** run the full shared release gate
 on the exact frozen/versioned final candidate. Include all32 calibrated OVR
 cases/direct ABI and automatic/manual/post-LineEditor replay continuation;
 no stale128 passes, relaxed requirements or silent ROM replacement.
- [ ] After that gate passes: proper new release, source/IPS/rollback/deployment
 policy and affected current website images/accurate video-version labels.

Today is targeted private testing only. Do not start the large matrix on Nov4.
[Source/evidence/scope](GREY_FEEDBACK_PREVIEW2_20261104.md).


## Grey framed menus —2026-11-04 private preview

Separately named local Beta preview897c575f… includes native grey panels,
centred trimmed help and the prior corner repair.91 real screenshots/21 groups
opened; matched transfer-sale SRAM, four read-only season saves,10 closed-menu
asset/palette controls, full72 period-logo tiles/all64 colours, two portrait
crops and exact rebuild checked. Full release suite intentionally not run at
jdc's explicit request. Public20793fce…/465 native/test sources stay unchanged.
[Exact source boundaries, evidence and limits](GREY_MENU_PREVIEW_20261104.md).



## Shorter native-sound feature tour completed —2026-11-04

New4:57/45-chapter current2026-11-03-r2 film replaces homepage9:33 tour.
All26 menu rows; three genuine~20sec Crits; actual charged directional pass,
Allstar CPU/black-red, dollar transactions/current roster/OVR/hot-cold and
explicitly labelled coherent synthetic standings/leaders.45 final export images
and27 public browser screenshots opened. Full decode, zero audio clipping,
six zero-lag PCM/AAC controls and1440/390/320 playback/seek/45chapters pass;
68 HTTPS checks pass. Old film/provenance archived unchanged. No native ROM,
controls or user saves changed; no native requalification/reset required.
[Movie/report/scope](VIDEO_SHORT_TOUR_20261104_R2.md).



## Penaltys50% investigation complete —2026-11-03-r2

Exact released20793fce… unchanged.24 matched simulation cohorts per setting:100%442/442 candidates admitted,50%241/468 (51.5%),227 filtered. Actual committed432 versus236; icing662 versus664. Cohort50% fractions39.1–71.4%,7–33 candidates each; no exact per-match/whistle-halving claim. Current live-input measurement observer E900/HUD collision exposed and old live counts withdrawn. Final exact-ROM host-only read observer validated72 whole emulated-state controls, with typed host-pointer ELF relocation and active clock/target negatives. Actual naturally completed Full/Light/Mini saves retain50% after reboot/lock attempts. No native game-rule fix or new ROM release required. [Full current audit](PENALTIES50_AUDIT_20261103_R2.md).


## Final current checkpoint —2026-11-03-r2 (Beta)

Released locally and publicly: SHA256 `20793fce15e640bf60d31c48e95bd0d284105fc07f8ae01ed08b1a579b37e99f`;
CRC32 `966FE68B`. **128/128 required groups pass on465 exact native/test
source hashes**, with the original failed full batch retained and four entire
affected groups requalified through an audited six-file host-test-only closure.
Twenty independent control/rollback builds, extracted-source exact rebuild and
original-base IPS reconstruction pass. No later website task changes this ROM.

Main-menu speed/sequencer continuity, exact root Games credits URL, eligible
downed-skater Y selection and consistent actual-roster team OVR are qualified.
Quickgame retains four human splits; Allstar additionally offers CPU and black/
red uniforms. Preseason transfers and the restored native presentation remain.

The separate image-to-title audit checks123 original image files/232 old placements,
corrects21 explicit bindings and inventories **239 final placements/128 image URLs**.
Fresh same-ROM captures show actual owned purchases, framed centred menu help,
dollar amounts, keeper possession/arrows, faceoff stars, Overall and a matched
172KMH shot/replay pair. Historical originals/notes/audit bytes are retained;
fixture scopes, current captions and manual limits are explicit.
Final public desktop1440/mobile390/320 browser evidence and image/body/link checks
are recorded in [the completed image audit](IMAGE_TITLE_AUDIT_20261103_R2.md).

The public market guide explicitly explains separate cash transactions, Free
Agents, no automatic player swaps/replacements or CPU recruitment, legal squad/
budget guards and a conditional McDavid/Draisaitl example. This is documentation
of existing behavior; the native transfer economy/rules remain unchanged.

Local installation preserves configs/remaps and138 unrelated saves. Three old
matching progress files were cleared only at this new release, after normal user
frontend exit. All three original runtime histories remain hash-preserved and
are merged into the current item. Legitimate new-version play is preserved.
Bodin delivery remains queued while the known host is offline; no remote install
is claimed. Physical controllers, extended human play/audio preference and live
jdc/Bodin multiplayer remain beta acceptance. The older9:33 film is clearly dated
2026-11-02-r2. Earlier dated sections below are historical snapshots, superseded
by this current checkpoint.


## Main-menu, explicit Y and actual-roster OVR —2026-11-03-r2

Main-menu-only sequencer service preserves complete checksums and continuous
music timing; maximum blocked frames24→10 for scrolling and69→57 for mode changes.
Both final traces match idle clock/channels exactly. A literal restart command
was not reproduced. Startup credits show https://games.jdc.se. Explicit Y admits
an eligible downed human's keeper request without healing or bypassing native
guards. Ordinary team presentations now use the actual roster mean before/after
transfers: fresh Edmonton57, improving trades61 (two sales) or62 (four sales).
Player attributes, OVR-based prices/budgets and season accounting remain unchanged.

**128/128 mandatory groups pass on465 exact native/test source hashes**;20 independent
full controls/rollbacks, extracted-source rebuild and original-base IPS pass.
123 actual native originals are opened, refreshing102 existing current picture IDs.
[Behavior/scope](MAIN_MENU_Y_OVR_20261103_R2.md) ·
[Exact qualification](QA_20261103_R2_LOGS.md) · [Native review](GRAPHICAL_FINAL_20261103_R2.md).
Local/public delivery and opened public desktop/mobile review: pending.
Physical controllers/live multiplayer and subjective feel remain manual beta limits.
Older dated sections below are historical snapshots, superseded by this checkpoint.


## Games domain and website structure —2026-11-03

Website restored at https://games.jdc.se/nhl94/; Manager/NHL94 logo links are
available at https://games.jdc.se/. Public images live in pics/, page code in
system/, other content in site/ subdirectories.909 image originals and historical
HTML retained;444 native/test source bytes and current ROM unchanged.2,564 initial
HTTP checks and30 individually opened desktop/mobile screenshots PASS.
[Structure and evidence](WEBSITE_LAYOUT_20261103.md).
Pending next: in-game startup URL and native menu speed/music restart investigation.
This website move does not release a ROM or reset progress.


## Transfer dollars and framed menu controls —2026-11-03

Transfer prices, budgets, purchases and sales now display dollar amounts with dot
thousands separators: one unchanged internal unit is $1.000. Initial $16.042.000
still exactly funds an OVR99 star plus an OVR79 middle player. The native economy,
rosters and save accounting remain unchanged. Main-menu help is centred inside
the existing native frame style; all 26 settings remain selectable through five
visible rows. QG-V01 was already repaired in r2; fresh reproductions correct the
previous stale unresolved label without changing Stars native code.

**125/125 required groups pass on 444 native/test source hashes**, with all original
failed batches retained and complete affected test-only requalification disclosed.
Fifteen independent controls/rollbacks, exact ZIP-source rebuild and original-base
IPS reconstruction pass. All 102 current website picture IDs use this final ROM;
123 native captures and 18 public desktop/mobile screenshots are opened and pass.
718 public HTTPS checks pass; historical assets are preserved.
The sound tour remains explicitly the authentic r2 recording.

SHA256 `7d1fc931f5a5a4958b1eefcc05f00661b5321a6a1b2404d7563c98a22b8eb262`; CRC32 `61F87B0A`.
[Behavior/scale/Stars erratum](USD_FOOTER_20261103.md) ·
[Exact qualification](QA_20261103_LOGS.md) · [Opened graphics](GRAPHICAL_FINAL_20261103.md).
Local/public delivery: installed and verified. Bodin: offline, authorized queue pending.
Physical USB/phone operation and subjective play feel remain manual.


## Final website and native-sound tour acceptance — 2026-11-02-r2

M24 complete:102 opened native images +36 opened public desktop/mobile pages.
M25 delivered with disclosed limits:9:33/71 native-sound clips, highest homepage
player;9 final public playback screenshots opened at1440/390/320. Actual H264/AAC
playback, nonsilent audio graph and71 chapter links pass. All50 public game-feature
sections +4 groups inventoried; not every edge case is filmed.
[Movie/coverage](../../video/) · [Playback report](VIDEO_FEATURE_TOUR_20261102_R2.md).
ROM remains qualified124/124 on443 sources, unchanged c68333a8422b6e3754350ef2abd646951d85f6e2c2cd0f987dff1f184aa53b17.
Local/site released; Bodin offline queued. No user frontend or new progress touched.
Physical USB/phones, subjective play feel/loudness and explicit unshown cases
remain limitations; inherited compact Stars shootout tie remains documented.


## Transfers, Allstar CPU and presentation —2026-11-02-r2

Preseason Transfers On/Off sits directly below Icing. Native OVR² prices and
budget16042 cover one star(85–99) plus one middle(60–79) at both upper bounds;
valid squads, nonnegative funds, Free Agents, atomic storage and played-season
locks are retained. The original native overall/hot-cold presentation is restored
with current ordinary, transferred and Allstar data. Allstar adds1vsCPU and keeps
four human splits with blackA/redB uniforms; Quickgame offers only four human
splits under the latest explicit policy. Ordinary32club uniforms stay exact.

HUD/menu/default refinements include right-side Momentum bars, framed above-clock
powerplay, clean replay, shared shot/pass charge, KMH/MPH, optional fragile glass,
permanent painted dots/OT rules, consistent readable page help and five native
0:40 beeps. Real Draisaitl line editing is repaired and permanently regression-tested.
The friendly planned-catch guard preserves native movement/physics and general
busy-shot exclusions; no new Hard strength search or win-rate claim.

**124/124 required checks pass** on **443 native/test source hashes**.
The complete same-ROM matrix plus complete affected test-only observer rechecks
and strict host-timeout retry are separately recorded, with original failed
batches retained.14 independent controls/rollbacks, byte-exact source/ZIP rebuild
and original-base IPS reconstruction pass.102 native originals are opened; current
website images replace77 old slots, while32logos/115historical assets are retained.
Local installation and public browser checks are tracked separately; Bodin delivery
requires connection/normal exit. Physical USB/play feel/audio salience stay manual.

SHA256 `c68333a8422b6e3754350ef2abd646951d85f6e2c2cd0f987dff1f184aa53b17`; CRC32 `4B82D65A`.
[Current behavior/pricing](TRANSFERS_REFINEMENTS_20261102_R2.md) ·
[Exact qualification](QA_20261102_R2_LOGS.md) ·
[Opened graphics](GRAPHICAL_FINAL_20261102_R2.md).
M25 native-sound video remains LAST after release and M24 public review.


## Replay HUD, Quickgame CPU option and menu texture — 2026-11-02

Automatic replay retains the measured shot's coloured bar, both speed units and
pre-replay lower-left clock/frame. REPLAY uses its own native VCR pattern bank,
so the speed text no longer corrupts the heading. Power-play captions retain
ownership of their clock interior, including during a temporary native redraw.

The mode is displayed as **Quickgame**. Its four human splits remain, alongside
**1 vs CPU** (P1 home; entirely CPU away), using ordinary pads or the adapter.
Allstar retains its four human-only splits and black A/red B uniforms. Games,
OT/shootouts and returning to an existing season retain native results/progress.

The supplied image 78 is used only for the menu background. The NHL94/Season
Edition startup picture and routine remain byte-identical, with matching real
startup screenshots. Native club/logo/font/panel palettes remain in use. P3 now uses bright neon
green; P1 blue, P2 red, P4 yellow and the red goal retain their entries. The
fixed green entry is shared with a few small crowd/background details, which
also brighten. Ordinary club and Allstar jersey palettes remain unchanged.

All **112 mandatory suites pass** on this exact ROM and **353 hashed sources**. The whole matrix ran freshly. Nine independent builds, exact R7 rollback, the pre-neon control, six matched controls and original-base IPS reconstruction pass. Original AutoReplayOn scenes: **38/40 raw-exact**; every partial native decoder sample is recorded and proven against the same packed record. Separate declared AutoReplayOff scenes: **40/40 raw-exact**. Ten active negative controls reject invalid changes. Physical USB/four-person play feel remains manual. No new Hard win-rate claim. Preseason transfers and the original season overall/hot-cold screen remain ordered next work.

ROM SHA256: <code style="overflow-wrap:anywhere">b29af4d9300da2f4d989e9537a0c94f2da43f701fdb6054f4c5b9ce5040fc3d8</code>; CRC32: `887FFBD3`.
See [REPLAY_QUICK_MENU_20261102.md](REPLAY_QUICK_MENU_20261102.md),
[exact QA](QA_STATUS.json), and [opened native images](../../audits/replay-quickgame-menu-20261102/).


## Native Allstar teams — 2026-11-01-r7

**ALLSTAR MODE** uses the same human-only 1v1, 1v2, 1v3 and 2v2 flow as Quickgame.
Two virtual teams contain 52 unique players selected by the actual native
zero-hot/cold overall calculation: best 6 G, 16 D, 10 C, 10 LW and 10 RW. A,B,B,A snake
allocation within each natural position gives each team 3 G, 8 D, 5 C, 5 LW and 5 RW,
with 8 valid native lines. Base attributes, names, portraits and critical-shot
ratings follow the real selected players. Ordinary 32 club records stay intact.

Allstar A/B replace inherited city-title bitmaps. Team overall is the floor of the mean of all 26 actual native zero-hot/cold player overall values (82/82); ordinary club ratings remain unchanged. Allstar A wears all-black uniforms and Allstar B wears all-red uniforms, in both native uniform banks. Original rink/centre-logo artwork remains. Native AI teammates fill
other positions; there is no entirely CPU opponent option. All four human
splits, B/Y control and actual pad rotation in tied-OT shootouts are checked.

All **106 mandatory suites pass** on this exact ROM and **339 hashed sources**. The entire matrix executed freshly. Independent rebuild, exact R6 rollback, six fresh matched controls and original-base IPS reconstruction pass. **36/40** ordinary 600-frame scenes are raw-exact to fresh R6; every difference and the complete older comparison chain remain recorded. No new Hard win-rate claim. Physical USB hardware and subjective play feel remain manual tests.
No new Hard win-rate claim. Transfers and the original overall/hot-cold screen
remain dependent work, not implemented by this release.

ROM SHA256:<code style="overflow-wrap:anywhere">6e66c0afe2496eb3f6a688c29888ca62b17aee0b0f51efec96d11dce5e842b31</code>; CRC32:`9F6EBE75`.
See [ALLSTAR_R7.md](ALLSTAR_R7.md), [exact QA](QA_STATUS.json), and
[opened native screenshots](../../audits/allstar-r7-20261101/).


## Human-versus-human Quickgame — 2026-11-01-r6

Native **QUICKGAME** offers exactly **1 vs 1,1 vs 2,1 vs 3,2 vs 2** human
controllers. P1 is home; P2 is away in1v1; P2–P3 away in1v2; P2–P4 away in1v3;
P1–P2 home/P3–P4 away in2v2. No CPU-only opponent option is offered. Native AI
teammates fill the other positions. A four-player adapter is required for3/4pads;
without it the menu/actual sides use1v1. Modern Y keeper selection and immediate
B switching cover both human teams. Tied-OT shootouts rotate real physical
pads on each side, including P3/P4, preserving native attempt/keeper/score rules.

Quick pregame, pause and result menus exclude Simulate Match, Abandon Season,
Season Standings and Scoring Leaders. Existing season records are preserved by
Quick matches and by return to Full/Light/Mini; this differs from the mandatory
fresh-progress reset performed when installing any new ROM release.

All **103 mandatory suites pass** on this exact ROM and **333 hashed sources**. Full matrix plus an independently repeated maintenance-report aggregation correction; unchanged checks retain their exact executed source bytes. Independent rebuild, exact R5 rollback, six fresh matched controls and original-base IPS reconstruction pass. Eight of40 ordinary600-frame scenes are raw-exact to fresh R5; every difference and the complete older comparison chain are retained. No new Hard win-rate claim.
ROM SHA256:`49c8b23b940a9c5368580dfcad11626a3ffbd4a51cdcae10ca8c9ce01f642487`;
CRC32:`F137D497`. See [QUICKGAME_R6.md](QUICKGAME_R6.md),
[exact current QA](QA_STATUS.json) and the
[dated native screenshot gallery](../../audits/quickgame-r6-20261101/).


## Momentum, lower-left shot meter and native positioning — 2026-11-01-r5

**Momentum: On/Off**, default On, controls the existing earned up-to-±10% boost.
Off removes scaling, consumes events and hides bars; Easy100/20/full energy is
independent. The dual-unit shot meter replaces the lower-left NHL badge, with
an automatic power-play-caption fallback. Native receiving and Hard defensive
arrival now retain actual tracking and braking without writing motion/results.

All **100 required suites pass** on this exact image and 325 hashed test/build
sources. The formerly retained receiver geometry check passes. Independent
rebuild, exact Nov1r4 rollback and original-base IPS reconstruction pass.
7/40 ordinary 600-frame scenes are raw-exact to fresh r4 controls; every
difference and the complete older comparison chain remain recorded. No new
Hard win-rate series. Earlier 90/100, 95/100 and 99/100 failures are retained.

See [MOMENTUM_LEFT_R5.md](MOMENTUM_LEFT_R5.md),
[MOMENTUM_LEFT_RESULT.json](MOMENTUM_LEFT_RESULT.json), and the additional
[request-by-request audit](FINAL_AUDIT_R5.md). Human keeper-outlet confirmation,
retained-variant preferences and unreachable Bodin delivery remain explicit.
ROM SHA256: `db244dd005f960a9923c44f0dc4168fb938af227a5e9aa97f24d376fd295291e`; CRC32: `99166AFE`.

## Optional painted-dot faceoffs — 2026-11-01-r4

Independent start-menu **Faceoff Dots: Off/On**, default Off. On maps the final
native stoppage point to the nearest painted point within its native region.
Off retains the inherited free neutral-zone point policy. Native formations,
puck placement, line selection, camera and renderer remain responsible.
Native and Full/Light/Mini save separately and seasons lock after match one.

The whole-formation displacement is reproduced on exact unmodified old/current
ROMs after a declared native queued minor: point(-14,-12), centres(-14,-27) and
(-14,3). On gives point(0,0), centres(0,-15) and(0,15). This matches the reported
appearance; the exact original incident history is not authenticated. Earlier
centre-only observers excluded displaced points. This is an optional coordinate
policy, not a claim of full NHL faceoff-location rules or a renderer repair.

All 97 suites execute:96 pass; retained finisher_hold remains failed with current
measurements disclosed. Strict gate FAILED. New geometry/ABI/save/menu/native
foul/lifetime tests pass. Independent rebuild, exact Nov1 r3 rollback, five matched
AI controls and original-base IPS reconstruction pass. 38/40 ordinary scenes
are raw-exact to fresh Nov1 r3; every difference and older comparison is retained.
See FACEOFF_DOTS_R4.md and FACEOFF_DOTS_RESULT.json.

ROM SHA256:c32a79e34127757b52cd488c124c9247bf7b875088bac89956a6b7ca2b132cf6; CRC32:AD52D7BA.

## Optional Hard AI shot planning — 2026-11-01-r3

Independent start-menu **AI Shot Plan: Off/On**, default Off. On lets a quiet
Hard CPU carrier defer a covered middle-distance ordinary shot, preserving
native passing and using two lateral approach targets when space permits.
Real keeper openings, close finishes, useful long shots and already buffered
one-timers retain native decisions. Pressure keeps native steering/evasion;
after 96 actual carrier-target observations the deferral ends. Native movement,
attributes, shot/pass physics, ownership and results remain responsible for play.

Native and Full/Light/Mini preferences save separately and lock after match one.
Normal/Easy and every active human pad are guarded. The accepted38-win base
remains complete; this optional experiment makes no new win-rate claim.

All 96 required suites execute:95 pass; the retained receiver-hold test remains
failed, with changed measurements explicitly recorded. Strict gate FAILED.
The original complete matrix and its Easy test-observer failure are preserved;
the entire Easy suite passes after correcting its loaded-actor packet predicate,
with256 native observer ABI cases and an active bad-skill negative control.
No cartridge byte changes for that correction. Independent rebuild, exact Nov1 r2
rollback, five matched AI controls and original-base IPS reconstruction pass.
21/40 ordinary scenes are raw-exact to fresh Nov1 r2; every difference and the
entire earlier chain remain recorded. See AI_SHOT_PLAN_R3.md and
AI_SHOT_PLAN_RESULT.json for behavior, all hooks, tests and limits.

ROM SHA256:0b4b104cf31e3b78d5f686349bbaad73ed54ba491c23c6ac661aca02eca396ca; CRC32:BD66F658.

## Optional overtime shootout roster and spin rules — 2026-11-01-r2

Independent start-menu **OT SO Rules: Off/On**, default Off. On rotates the
available native skaters before reuse, excluding every actual goalkeeper
and native injury states. Both clubs may reuse when the smaller eligible pool
has shot. Actual native roster/attribute loading, attempt advancement, goals,
keeper paths and single-miss accounting remain responsible for the result.
A full signed body rotation before the shot ends the attempt. Facing uses its
complete native16.16 angle, so a nearly complete spin remains legal.

This applies to the tied-overtime extension; native standalone Arcade Shootout
keeps its original roster behavior. Native play and Full/Light/Mini save
separately; seasons lock after match one. Existing auto-start, progress,
single-shot and controller behavior remain independent.

All 95 required suites execute:94 pass; only the inherited receiver-hold
geometry failure remains. Strict gate FAILED. 4 complete affected suites
were requalified after scope/navigation test corrections; the first full matrix
and every original failure remain retained. Independent rebuild, exactNov1
rollback, five matched AI controls and original-base IPS reconstruction pass.
40/40 ordinary scenes are raw-exact to fresh Nov1; every earlier comparison
and retained difference remains recorded. No Hard win-rate search.
See STRICT_SO_R2.md and STRICT_SO_RESULT.json.

ROM SHA256:984854649bb9528f9dfc2200d43237cb2a3ab2cbe5cc847fde1ef10d375e660f; CRC32:95D98D33.

## Optional alternative goalkeepers — 2026-11-01

Independent start-menu **Alt Goalies: Off/On**, default Off, implements an
experimental reach21 and catch/rebound model from floating#217606. On keeps
native keeper animations, ownership, saves and outlets; real released shots
may produce cornerward, short/long rinkward or slow/fast goalward rebounds.
Distance and screening change the explicit experimental outcome distribution.
Rebound speed is conservatively capped by incoming horizontal speed. A same-shot
rebound entering the net counts one native goal, one SOG and one keeper SA.
Native play and Full/Light/Mini save separately; seasons lock after match one.

All94 required suites execute:93 pass; only the inherited receiver-hold
geometry failure remains, so the strict gate is FAILED. One complete pass-mode
suite was requalified on the sameROM after extending its actual-input window;
the first full matrix and its P3 coverage failure remain retained. Independent rebuild,
exact r11 rollback, matched AI controls and original-base IPS reconstruction
pass. 19/40 ordinary scenes are raw-exact to fresh r11 controls; every
difference and earlier parent chain remain recorded. No Hard win-rate search.
See ALT_GOALIES_20261101.md and ALT_GOALIES_RESULT.json.

ROM SHA256:331a12164cd1102d4dbbaecea21d7ea12d7f710b79d9c6eaa971cb7d31bc1840; CRC32:C1A1B3B5.

## Optional pass pickup delay — 2026-10-31-r11

The start menu now has an independent **Pickup Delay: Off/On**, default Off.
On adds a minimum six native updates of contact cooldown to active skaters on
both teams when an ordinary human B pass is actually released. During this
short interval skaters cannot take stick possession or make body deflections.
Goalkeepers, boards and nets retain their native response. Existing longer
cooldowns stay intact; CPU passes and requested CPU keeper outlets stay native.
This is an experimental passing choice, separate from all other pass settings.
It saves separately for native play and each season and locks after match one.

All 93 suites execute: 92 pass; the inherited receiver-hold geometry failure
remains failed. Strict gate: FAILED. Four affected complete suites were
requalified on this unchanged ROM after observer corrections; the first complete
matrix and every original failure remain recorded. Rebuild, exact r10 rollback, matched AI
controls and original-base IPS reconstruction pass. All 40 ordinary scenes are
freshly compared with r10: 40 are raw-exact; every difference and the earlier
comparison chain remain recorded. No Hard win-rate search resumes.
See PICKUP_DELAY_R11.md and PICKUP_DELAY_RESULT.json.

ROM SHA256: 3d1f54ae96b0308163474b8c138be9ed880fa96d2213e871a8e77e71bdbcccc5; CRC32: E07B3785.

## Hold/release passing and corrected controller stars — 2026-10-31-r10

Start-menu Pass Charge Off/On, default Off. With On, ordinary owned human B
passes wait for release. Holding increases native pass power from25%to100%,
capped after30 owning input updates; the cap never auto-releases. Aim can change
while held. Assisted/Directional, both teams, P1–P4 and manual keepers are
covered. CPU keeper requests and B switching without puck stay immediate.
Height/Speed/Cone/Mode/Penaltys remain independent. Native and each season save
separate charge records; the first-match season lock still applies.

Physical stars: **P1blue, P2red, P3green, P4yellow**. This is jdc's final mapping,
superseding the earlier P2green/P3red request. The original red goal is restored.
All fixed rink palette bytes and the native rendering instructions are original;
P3 uses its own identical-shape green art in proven low-ROM padding. Its signed
native tile key is validated through actual cache/DMA/VRAM/SAT rendering.

All92 suites execute;91pass and the retained finisher_hold geometry failure
remains failed. Strict gate:FAILED. Six complete affected suites were
requalified on the same ROM after test-only record, observation and report
corrections; every original functional criterion stays covered. Fresh rebuild,
exactr9rollback, matched AI controls and original-base IPS reconstruction pass.
All40 ordinary scenes are freshly compared with r9;40are raw-exact, with
all earlier r8-and-prior comparisons retained independently. No Hard win-rate
search resumes. See PASS_CHARGE_COLORS_R10.md and PASS_CHARGE_COLORS_RESULT.json.

ROM SHA256:5e3d4aedcf006f2c92dacdae965f79b15dfd078fae6a0862f77b6520a60d1eee; CRC32:0301E4D0.

## Easy assistance and distinct controller markers — 2026-10-31-r9

Easy gives the entire human club maximum skills (100-equivalent), the CPU-only
opposition fixed20-equivalent skills, and refills all26 human roster energies
to4096 every game frame. Keepers, AI teammates, co-op and native roster
substitutions are covered. Native energy/physics routines are unchanged.
In-game skill/Overall columns actually draw100/20, bypassing the native low-
rating lift only in Easy. Weight/jersey/handedness retain their native identity;
awareness uses the correct inverted timers. Normal/Hard, Crit Off, defaults and
season settings locks retain their established behavior.

P2 green, P3 red, P4 yellow; P1 retains its original appearance. Static star art,
source indices and three fixed rink palette entries change. No runtime drawing
hook or new RAM/SRAM. All869 native sprite frames retain their shape; only the
three requested marker frames change indexed artwork/attributes. Team uniform
and palette0 rink-line colors are untouched; edge pixels sharing those three
palette1 entries take their new hues.

All91 exact-source suites execute:90pass, only the retained finisher_hold
geometry failure. The strict release gate remains FAILED. All original positive
criteria pass on the final image. The first complete91matrix was rejected for
7new failures; its exact ROM/source/results remain recorded. Rebuild, exactr8
rollback, matched AI controls and original-base IPS reconstruction pass.
All40 ordinary scenes are recorded against fresh r8 controls;33 are raw-
exact and every difference plus the complete earlier audit chain is retained.
No Hard win-rate search resumes. See EASY_COLORS_R9.md, EASY_COLORS_RESULT.json
and QA_STATUS.json for precise field scales, native tests and limitations.

ROM SHA256:443c96a23923e96529120cd19c486db1483101896787c76a8d2082e5836e6186; CRC32:9108C83B.

## Separate penalty frequency and preserved native rules — 2026-10-31-r8

The new final start-menu row is Penaltys: 100% / 50% / Off, default 100%. The original
combined Penalties option is renamed Rules and keeps Off/On/On except offside.
The percentage affects ordinary foul admission and whole new fighting incidents;
icing, offside and penalty-shot eligibility retain their existing controls.
A suppressed injury foul uses the native injury-only stoppage instead of losing
its report. Existing pending calls are not rerolled or erased.

All five independent percentage/pass choices save per season and lock after the
first match; native play has separate preferences. No extra SRAM bytes are used.
Buffered human contact-to-shot setup now runs as one protected transition, so an
intervening native sprite update cannot change the measured stick hotspot. The
old42px failure is reproduced independently; native register/CCR/IRQ-mask/stack
contracts are compared at every IRQ level. All 89 exact-source suites execute: 88 pass, only the retained finisher_hold geometry
failure, strict gate FAILED. Independent rebuild, exact r7 rollback and original-
base IPS reconstruction pass. See PENALTYS_R8.md and PENALTYS_RESULT.json.

All 40 ordinary 600-frame scenes are recorded against fresh exact r7 controls;37 are raw-exact. Every difference and the complete earlier audit chain remain retained. No Hard win-rate investigation resumes.

ROM SHA256:501da8aaf8fc1fb4f5fdd22f4d8f8d893a8fe06de9ec28cb4ecaf0a6e18297eb; CRC32:F2C8334E.

## Optional directional passing and vector targeting cone — 2026-10-31-r7

Two new start-menu rows: Pass Mode Assisted/Directional (default Assisted), and
Target Cone Default/15 degrees/30 degrees/45 degrees (default Default).
Directional sends ordinary human B passes along the actual eight-direction aim,
without selecting a receiver or transferring control at release. The explicit
cone choices limit Assisted receivers using actual vector half-angles; Default
retains the previous native octant policy. P1–P4 and manual keepers are covered;
CPU keeper outlet requests retain their requested receiver. Height and Speed
remain independent. Seasons save all four choices and lock after the first game;
standalone preferences are separate. Hold/release charging remains research.

The first shared-path prototype reduced AI finish releases in the gameplay test
and was rejected. The final design hooks human B input and existing human-only
scan branches, preserving the CPU pass/scan/outlet instruction paths exactly.
All 40 ordinary 600-frame scenes match fresh r6 controls byte-for-byte; the earlier
comparison chain is retained. This is bounded regression evidence, not a claim
that every possible game is identical. No Hard win-rate investigation resumes.

All 88 shared suites execute: 87 pass and the retained receiver-hold geometry check
fails, so the strict release gate remains FAILED. No new regression is waived.
Independent rebuild, exact r6 rollback and original-base IPS reconstruction pass.
See PASS_MODE_R7.md and PASS_MODE_RESULT.json for code, tests and delivery.

ROM SHA256: 8172a7e4b3d55754076d4f31de2def263d423bad832b2057538fa3b5c1ca86c7; CRC32: 5DC93C86.

## Fair automatic co-op pickup control — 2026-10-31-r6

When a free CPU skater on your team takes possession, eligible active human pads
now share a random chance to control that skater. Native P1 priority no longer
wins every such co-op pickup. Only actual accepted pickups are considered.
Already controlled receivers, requested keeper outlets, manual keepers, pending Y,
locked skaters, single-human teams and special modes retain their native paths.
Other pads keep their existing skaters. There is no new menu setting here.

The hook at1538A calls a bounded helper at2EFC00. It uses the real controller-side
map, native RNG and native C0F8 handoff, with temporary eligibility data on the
stack. No RAM/SRAM field, forced pickup, rating or movement change is added.
Native tests cover316 assignment contexts,36 unchanged guards,112 paired ABI cases,
32 full collision contexts and48 physical-pad input branches.24 advancing-RNG series
exercise 144000 controlled native pickup draws, exactly matching the native RNG
control. These are automated fixtures, not 144000 matches or a hardware playtest.

All 87 shared suites execute;86 pass and the retained finisher_hold geometry failure
remains, so the strict gate is FAILED. All 40 current ordinary scenes and fresh r5
controls are recorded, including timing differences. Rebuild, exact r5 rollback
and original-base IPS reconstruction pass. No new Hard-strength claim.
See FAIR_PICKUP_R6.md and FAIR_PICKUP_RESULT.json.

ROM SHA256: 548615ad01079d6a93fc62a0409b4a8d6ec4e53676f8a629081f8cc34da712fa; CRC32: 26911EBB.

## Independent pass speed and safe standalone preferences — 2026-10-31-r5

The last main-menu row now offers Pass Speed: Normal / Slow, default Normal.
Slow reruns the native intercept calculation at80%of nominal pass power, keeping
receiver movement, native time bounds and catch timers in the calculation.
It never chooses an earlier arrival. Fast receivers outside that lower-power
range retain their native pass. This is not an exact20%reduction for every pass.
Pass Height Normal/Low remains independent, using the original arrival estimate
and preserving native fractional lift. Both teams and keeper targeted B passes
share these settings; A flips, C shots and blind-pass code retain their paths.

Each season saves both choices and locks them after the first completed match.
Standalone play has separate preferences. A paired native test also exposed
r4's standalone height record at2E20overlapping Full-season goalie data. Both
standalone preferences now use4D20in Light's excluded shared-profile gap; menu
writes preserve Full goalie bytes and survive slot circles and cold boots.

Native tests cover120direct clamp ABI cases,1152Normal/invalid full solver ABI
comparisons,384Slow comparisons against the original native lower-power solver,
34real menu/lifecycle cases, paired r4/current goalie-storage reproduction and
four co-op input games covering both speeds and heights. All86shared suites run;
85pass and the retained finisher_hold geometry failure remains, strict gate
FAILED. All40current ordinary scenes and fresh r4controls are recorded; additional
instructions may change native HV-counter decisions. No unchanged-gameplay,
pass-completion or new Hard-strength claim. Rebuild, exactr4rollback and original-
base IPS reconstruction pass. See PASS_SPEED_R5.md and PASS_SPEED_RESULT.json.

ROM SHA256:40f388dc0beaedbd0883fdd4101a30debfb0fbbe62aab9d7a382e61476d513e0; CRC32:F82519B6.

## Optional pass height — 2026-10-31-r4

The main menu now has Pass Height: Normal / Low, default Normal. Low lowers the
vertical lift of targeted B passes on both teams, including keeper passes.
Short passes retain their existing lift. The native horizontal velocity,
arrival-time estimate and receiver intercept arithmetic remain unchanged.
A flips, C shots and blind passes retain their code. Lower lift can change
landing and interception; no pass-completion or competitive balance claim.

Full, Light and Mini save their own setting, which locks after the first
completed game and survives a reboot. Native exhibition/playoffs have a separate
preference. Missing/corrupt records default Normal without unlocking a season.
Confirmed season restart resets only that season to Normal.

Native tests cover80paired register/CCR/stack/fraction contexts,64moving-target
contexts across5profiles,28controller/menu/slot/boot/lock/restart cases, and real
co-op directional/B input with both Normal and Low passes above the lower cap.
The full85-suite exact-source matrix has84pass and the retained finisher_hold
geometry failure; the strict gate remains FAILED. Forty ordinary native scenes
and fresh r3 controls are recorded; added dispatch timing may change HV-based
decisions, so no identical-gameplay claim. The earlier strict chain is retained.
Independent rebuild, exact r3rollback and original-base IPS reconstruction pass.
No Hard win-rate investigation is resumed. See PASS_HEIGHT_R4.md and
PASS_HEIGHT_RESULT.json for the exact evidence, scope and delivery status.

ROM SHA256:0f7f080a30b52e536d7107ffc75044c21c1c617aa5d0007ad6203349604e2a69; CRC32:4B4E78D6.

## Downed pass receivers — 2026-10-31-r3

Falling/downed skaters are no longer selected by the native B-pass search or
keeper fallback, and cannot take stick possession while the fall/recovery
animation remains active. A receiver knocked down after launch is protected too.
The puck can still hit the body and deflect. Standing windups/one-timers, keeper
dives/catches and skaters who have recovered retain their native eligibility.

Two pass-scan eligibility tests and the existing actual stick-contact gate call
one small helper. No general frame/animation hook, RAM field, forced movement,
physics, rating or RNG write is added. The original pickup/crit gate and native
body response remain. Additional instructions can alter native HV-counter timing,
so ordinary game trajectories are recorded rather than claimed byte-identical.

The paired native tests cover84CCR/register/stack contexts,114real receiver
searches and44full stick/body collision cases. A native co-op input test injects
only a synthetic receiver fall after an actual pass: the parent shows6downed
possession episodes (579observations); current has zero. Recovery and subsequent
legal possession occur naturally. These are not physical-controller recordings.

All84shared suites execute:83pass and the retained finisher_hold geometry failure;
the strict gate remains FAILED. All40current ordinary scenes and fresh exact
r2controls are recorded, including every difference. The complete earlier strict
comparison chain remains on those parent controls. Rebuild, exact r2 rollback
and original-base IPS reconstruction pass. No Hard win-rate search is resumed.
See DOWNED_RECEIVER_R3.md, DOWNED_RECEIVER_RESULT.json and QA_STATUS.json.

ROM SHA256:40b3a5700ae7c3ce6f0536b878a22b2e7284a8e418231ed6a6ea4ce891fb3548; CRC32:16DD6925.

## Shootout puck progress and automatic start — 2026-10-31-r2

Shootout attempts now start without a controller shortcut. The tied-OT fixture
reaches the first live human attempt with no input in180frames (about3seconds),
previously619frames. A player may react before starting the approach; the normal
attempt timeout still applies. Human and CPU countdowns both use60native ticks.

Before a shot, the puck must keep moving toward the opponent's goal. Substantial
retracement, a stopped approach or crossing the goal line outside the mouth ends
the attempt through the existing native miss event. Body orientation alone is
not a foul: an unshot loose puck still moving forward may be recovered. Stick
movement uses an8-world-unit retracement allowance and12non-forward native
observations of stop tolerance. These are game tolerances, not literal NHL units.

Native shot/windup, loose-shot retouch, keeper ownership and shot-flight handling
remain. The native post-shot retouch queues one ending; no general pickup or
physics hook was added. This is an attempt-control fix, not complete NHL roster,
spinorama or eligibility parity. Custom OT lengths and season formats remain.

All83 exact-source suites execute:82pass and the identical inherited
finisher_hold geometry failure; strict gate remains FAILED.54paired native
boundary/ABI contexts,28both-end native-input cases and the no-input starts pass.
All40 ordinary raw scenes match40fresh2026-10-31 controls exactly. Earlier net
impact differences versus r13 remain independently audited. Rebuild, exact
2026-10-31 rollback and original-base IPS reconstruction pass. No Hard search.
See SHOOTOUT_RULES_R2.md, SHOOTOUT_RULES_RESULT.json and QA_STATUS.json.

ROM SHA256:dc279e63f35b2607ebba4eb5fb439eeb47f36239bddcfaae7c3d24e29f99cb08; CRC32:90081498.

## Less sensitive goal nets — 2026-10-31

Goal nets now require a50%higher impact-speed component before the existing
native dislodgement can trigger:8192→12288 in either direction on either axis.
Ordinary collisions keep the solid-net response; a heavy burst can still move
it. Both ends and every skater/keeper use the same rule. Four threshold immediate
bytes change; instruction timing, random gate, puck proximity, native release
velocity, stoppage, scoring and reset code remain exact.

A genuine CPU-play sequence reproduced one old dislodgement with40moving-net
observations; the corrected sequence has none. A real human burst reaches13868,
above the new threshold, with its input/motion trace exact to r13. The native
helper passes368paired boundary/actor/end/direction/guard/ABI contexts. No user
recording or physical-controller playtest is claimed.

All82 exact-source suites execute:81pass, only the identical inherited
finisher_hold geometry failure. Strict gate remains FAILED. All40 scenes are compared with fresh r13 controls: raw-exact, or exact until
a verified native net release. Post-impact differences are recorded. Rebuild,
exact r13 rollback and original-base IPS reconstruction pass. No new Hard win-rate series. See NET_CONTACT_20261031.md,
NET_CONTACT_RESULT.json and QA_STATUS.json.

ROM SHA256:aa035e55db8b43d1d38f3b178a739cc0edfab1fb8ef7837de718248fde5721f1; CRC32:BF43956E.

## Manually controlled keeper passes — 2026-10-30-r13

A fresh pass press while your manually selected keeper holds the puck now
reaches the native keeper passing routine. The immediate B-switch correction
previously treated an owned keeper as a switch request. Owned skaters retain
their native pass path; without possession B still switches to an eligible
skater nearest the puck. Automatic keeper catches and P1–P4 CPU-outlet requests
retain their established behavior.

Only the early ownership branch in switch.s and fourgoalie.s changes. There is
no new RAM, global hook, pass target override, physics, AI or season change.
Mini remains seven games. Native control may move to a receiver when the pass
is released; this is separate from switching away before executing the pass.

All81 exact-source suites execute:80pass, only the identical inherited
finisher_hold geometry failure. Strict gate remains FAILED. All40 current raw
ordinary-game traces match40 fresh r12 controls. Independent rebuild, exact
r12 rollback and original-base IPS reconstruction pass. No Hard strength series.
See MANUAL_KEEPER_R13.md, MANUAL_KEEPER_RESULT.json and QA_STATUS.json.

ROM SHA256:3070c2bbac4ec9fe6cf578c7fd0a3260869371a232b05ed76f33e44ca71ea899; CRC32:4D62BA8E.

## Mini is seven games — 2026-10-30-r12

Mini now has seven regular-season games: one against each of the seven other
clubs in your division. The new-game menu shows1/7, the saved length is7 and
all32 clubs finish with7GP. The eighth return fixture is no longer scheduled.
Single-player home/away alternation and co-op home games retain their policy.

Qualification and the season-summary boundary use the seventh game. Mini
retains one game per playoff pairing; Full remains best-of-seven and Light
retains its own schedule. Existing played legacy eight-game saves retain their
original length, lock and eighth-game cursor limit. Changed releases still use
fresh progress; an already running older ROM is left alone.

All80 exact-source suites execute:79pass, only the identical inherited
finisher_hold geometry failure. Strict release gate remains FAILED. All40 raw
ordinary-game traces match40fresh r11 controls exactly. Rebuild, exact r11
rollback and original-base IPS reconstruction pass. No new Hard strength series.
See MINI_SEVEN_R12.md, MINI_SEVEN_RESULT.json and QA_STATUS.json.

ROM SHA256:2ca0be4ef69f1d875c5a25e1ef91e42bda6b87396c5471fa8a417eb652ec8d60; CRC32:FB7FA2F0.

## Fouled player keeps the penalty shot — 2026-10-30-r11

P1–P4 now retain the penalty shot awarded for a foul against their controlled
breakaway attacker. Native setup can move the shooter to another object slot;
the physical controller is captured when getBAplayerInfo accepts the award,
then used when the two penalty-shot participants are assigned. Other attacking
pads cannot claim the shooter first. Existing team-side settings are restored
before returning from setup; normal assignment resumes after the attempt.

This release addresses priority1 only. Manual-keeper passing, downed receivers,
fair random unassigned pickup ownership, Penaltys100%/50%/Off and seven-game
Mini remain queued in TODO.md. Mini currently still has eight divisional games.

The native capture/setup, both teams and P1–P4 input/return regressions pass.
The full79-suite exact-source matrix has78pass and the identical inherited
finisher_hold failure; strict gate FAILED. All40 current ordinary-game raw
traces match40 fresh r10 controls exactly. Independent rebuild, exact r10
rollback and original-base IPS reconstruction pass. No new Hard strength
series or39/50 qualification is claimed. See PENALTY_OWNER_R11.md,
PENALTY_OWNER_RESULT.json and QA_STATUS.json for exact evidence and limits.

ROM SHA256:8f84f537ab945ea849ffc69eeba6794e9e1651556d3f878648ed846f8ebc4703; CRC32:41B60F46.

## Human one-timer stick contact — 2026-10-30-r10

jdc reported that receiving a pass while repeatedly shooting launched the puck
far from the stick. Native co-op input tests reproduced buffered releases up to
36.8 game pixels from GetHot's stick hotspot, with the shot animation at0.
The human buffer now waits for actual stick contact (radius4), then starts the
native contact/follow-through phase24. A fresh owned C cannot start a competing
ordinary windup while that request waits, and another pad's C cannot erase it.

The request stays responsive to native movement and possession loss. No puck
teleport or pickup-radius change is used; AI and normal shot physics retain
their code. Mini's divisional schedule, OT, momentum, defaults and locks remain.
See HUMAN_CONTACT_R10.md, HUMAN_CONTACT_RESULT.json and exact QA_STATUS.json.
The inherited finisher_hold geometry limitation remains failed; no new Hard
strength series is claimed. SHA256:b28fa2c8f42bd792f3118bc3d71df2fed5f959957d4adc60f372f2d4863c2038; CRC32:F157EFEB.

## Division-only Mini season — 2026-10-30-r9

Mini now plays eight regular-season games exclusively within the selected club's
own division. Its first seven rounds cover every divisional rival once; round8
is a return against the round4 opponent. All four simulated divisions use the
same eight-team round robin. Every team still plays eight games. Existing
single-player home/away alternation, co-op home fixtures, season locks and NHL
playoff qualification remain. Full and Light retain their league-wide schedules.

jdc reported Edmonton meeting Detroit in Mini. Exact r8 reproduces this from a
saved first-opponent choice; r9 restricts the cursor and actual fixtures to the
Pacific division. See MINI_DIVISION_R9.md, MINI_DIVISION_RESULT.json and current
QA_STATUS.json. The pre-existing receiver-hold geometry limitation is retained;
no new Hard win-rate series is claimed. ROM SHA256:29672052309b0bcad831716cd0edf35b498a265f0063d99173c70dff4df47c3a; CRC32:FD1A8A3A.

## Selected-length overtime and whistle momentum — 2026-10-30-r8

Overtime now uses the selected period length:5,7,10 or20 minutes. Native sudden
death and the existing shootout after a tied overtime remain. Shots/checks while
the clock is stopped, during a faceoff, pause, foreign view, replay or Stars no
longer earn crowd momentum or rating boosts. Blocked counters are consumed
immediately; resuming cannot bank them. Existing earned momentum is retained.
Normal and Hard retain the same earned-meter rules; Easy retains fixed ratings.

Changes are confined to shootout.s and crowd.s, plus release strings/checksum.
Rebuild, exact r7 rollback and original-base IPS reconstruction pass. All shared
checks are freshly run against this image; see OVERTIME_MOMENTUM_R8.md and
QA_STATUS.json for the exact results and retained AI limitation. No new Hard
strength matches are claimed. ROM SHA256:083f45ffbfff3986d55fbe2ea13384916c4c5b4f1d0b6c5e21e39aa80af80e02; CRC32:D813665F.

## Icing On default restored — 2026-10-30-r7

jdc clarified that **Icing must default On**, despite Off in the previous menu
screenshot. Fresh boot, empty Full/Light/Mini formats and explicit restarts now
use On. All other r6 menu defaults and keeper/current-skater outlet behavior stay
unchanged. Manually chosen saved settings and played-season locks still apply.

Only three default immediate bytes change1→0 outside version/checksum fields;
all other game code is byte-identical to r6. The 75 shared checks ran:74pass,
only the known inherited finisher_hold geometry failure; strict gate FAILED.
All40 current raw no-B scenes match fresh r6 controls exactly. The older r6/r5
observation audit remains separately disclosed. Rebuild, exactr6 rollback and
original-base IPS reconstruction pass. See ICING_DEFAULT_R7.md and QA_STATUS.json.
SHA256 48af31f9f7c85766951bf42745360571d6e7bf025bdce936363a58a178ab05f6; CRC32 273334B0.

## Keeper catch handoff and requested defaults — 2026-10-30-r6

A keeper already selected before catching the puck now returns to CPU control
on the accepted catch, and that pad receives an eligible free skater. The native
AI marker follows this ownership change. P1–P4 request an outlet to their current
skater; pass no longer switches that requester first. Explicit Y after the catch,
CPU-only keepers, opposing human teams and special modes retain their rules.

Fresh defaults follow jdc's latest screenshot: Auto lines, Fighting Off, Hard,
Auto Replays Off, Crit Shots Off and Icing Off; Edmonton remains the default club.
The screenshot's Icing Off supersedes the earlier r5 On request in this release.
Existing valid saved choices and played-season locks remain readable.

All75 shared checks ran:74pass,only the inherited finisher_hold geometry failure.
Strict gate remains FAILED. New coverage includes44 native ABI/object guards,
148 four-pad keeper cases (68 added handoff/current-requester cases),5 branches
of the reported r5 catch and6 native default/menu-render cases. All40 current puck/clock/action timelines match fresh controls.35 raw traces
are exact;15 isolated position/cache/render observations across5 traces are
recorded separately with exact whole rows before and after each difference. Rebuild, exactr5 rollback and original-base IPS reconstruction pass.
No new Hard win-rate series. See GOALIE_CATCH_R6.md and QA_STATUS.json.
SHA256 74f099b10459e000b4b50870e41617983641445f83c813328dba019b6b02fd7d; CRC32 25BBB406.

## Icing switch and Edmonton defaults — 2026-10-30-r5

Icing has its own **On / Off** row at the bottom of the start menu, after
Crit Shots. **On is the default**, explicitly confirmed by jdc. Off suppresses
the native touch-icing award. It follows the ordinary season-settings lock
after the first completed match and is saved independently for Full/Light/Mini.
Fresh startup, new empty formats and an explicit restart default to Edmonton;
manual team selection and the identity of an ongoing saved season still work.

All 72 shared checks executed: 71 pass; only the inherited finisher_hold geometry
failure remains. The strict gate remains FAILED. The new suites cover 34 native
menu/persistence/default-team cases and 56 synthetic native rule cases. Forty
current no-B scenes match fresh r4 and r3 controls exactly; all40 fresh r2 controls
still reproduce the immutable original traces. Rebuild, exact r4 rollback and
original-base IPS reconstruction pass. No new Hard win-rate series was run.
See ICING_MENU_R5.md and QA_STATUS.json for scope and exact-source evidence.
SHA256 a7e8bef0e8c78da6d37eacb432769af3f6d4b99e0196e451875c7991656ca182; CRC32 33894753.

## Keeper pass request at the possession deadline — 2026-10-30-r4

A timely pass request could switch the skater yet lose the keeper outlet to the
native possession timeout before it was processed. The first accepted request
now reserves up to four native processing ticks when the counter is still
nonnegative. Holding B does not refresh that reservation. An already queued
native timeout, negative counter or actual whistle retains the native outcome.
On jdc's controller this is physical A, the game's logical B. Y keeps its outlet
behavior. Native passing, target choice, physics, ratings and the r3 keeper
correction remain. No new Hard-versus-Normal strength series was run.

Paired timing tests: 1,792 valid requests per ROM; r3 passed 1,776 and r4 passes
all 1,792. All 128 late/stopped requests remain without a pass. The 1,920 cases
per ROM vary two native catches, timing and 1/2/5/12-frame taps; they are not
independent matches. Shared regression: 38 native ABI/object/guard cases and
108 paired input contexts pass. All 70 matrix checks executed: 69 pass; only the
same inherited finisher_hold geometry failure remains. Strict gate: FAILED.
The affected replay check was rerun after declaring the input modules; all
40 current no-B trajectories exactly match fresh r3 controls, and all 40 fresh
r2 controls still match the immutable original scenes. The original rejected
scope check is retained. Five rebuild/rollback comparisons and original-base
IPS reconstruction pass. See OUTLET_DEADLINE_R4.md and QA_STATUS.json.

SHA256 4dd53440a7a4b4b7920a432b1e70a18e0ddb43e2a57a553b65cd052a0a0dca50;
CRC32 4884D486. Fresh progress for this release; remaps/config/history retained.
The intermittent faceoff alignment report is still open.
Local r4 delivery and fresh-progress cleanup completed; remaps/config/history
migrated with hashes. The game was started with input recording. Bodin r4 is
queued for connection; the superseded r3 worker was stopped.


## Reported: goalkeeper pass command only changes skater (2026-10-30)

jdc reports that requesting the puck from his keeper now changes only the
controlled skater. Open report; no code or ROM change. All 41 existing ordinary
keeper-request cases pass. A private BSV2 reader recovered 20,679 complete input
frames and the initial native checkpoint from the recorded playtest. Important
limitation: this core's checkpoints omit SRAM, so the older read-only battery
clone does not establish exact playback of the incident. Do not claim it does.

Seventy-two native replay branches using two captured keeper possessions compare
B, C and Y with different delays/tap lengths. B and Y each produce 18 native
passes out of 24 cases; all their misses follow the native whistle. C produces
no keeper requests. A late B can still select a skater after that whistle.
These results identify behavior to inspect, not a proven cause or a released fix.
The installed SDL2 profile matching the attached Generic X-Box pad (12068:323)
labels physical A as RetroPad B / Genesis B, and physical B as RetroPad A /
Genesis C. The user subsequently confirmed physical A; see the r4 correction above.
Keep the new user report separate from the earlier passing test successes.
Current r3 SHA3453b3f... and strict68/69 QA remain unchanged. Further recording
must preserve the initial SRAM as well as ROM/core/settings and input history.
Private evidence: `.workspace/work/goalie-b-outlet-20261030/RESULT.json`.


## New report: intermittent faceoff alignment (2026-10-30)

jdc reports the whole formation left/down from the centre spot during PP2/PK2;
first occurrence noticed. Screenshot retained. At his request, automated state
replay expanded the investigation: 300,000 restores measured at 6.894 seconds;
36 further native faceoff observations; 6,144 replay plans, repeated exactly
with sprite checks (5,447 distinct source/input combinations). Each pass ran
1,532,592 native frames and 21,876 readable centre checks. An additional
frame-by-frame audit of 549 existing plans found shared camera/render offsets,
not relative rink/player displacement. Exact report still not reproduced;
PP2/PK2 centre transition remains a coverage gap and next target. User incident
state would help (F4), but automated investigation does not require one. No ROM
change; r3 and its existing QA remain unchanged. See FACEOFF_ALIGNMENT_WIP.md,
TODO.md and private stress/RESULT.json. Do not restart the stopped AI search.


## CPU keeper lateral-carry correction — 2026-10-30-r3

CPU keepers follow the actual carried puck instead of the body/stick midpoint.
If an opposing carrier moves across a committed lateral save, its stale final
hold is capped at eight native ticks and its cached movement choice is cleared.
Natural recovery, manual keepers, real loose-shot saves, catches and physics
retain their rules. Crit/B/Stars fixes from r2 remain.

All 69 exact-source checks ran: 68 passed. The inherited receiver-hold positioning check still fails, so the strict release gate remains FAILED. This is the same documented AI limitation retained in the user-selected playtest.
The new keeper suites pass 82 native ABI/object/guard cases and 48 paired live
approaches: r2 10 goals/15 catches, current 4 goals/22 catches. This is not a new
win-rate series. Five independent rebuild/rollback comparisons and original-base
IPS pass; only 219 declared bytes differ from r2, including header/checksum.
All 40 old r2 control traces were freshly reproduced and all 40 current scenes
recorded; current trajectories may differ because keeper behavior changed.
See GOALIE_LATERAL_R3.md and QA_STATUS.json. Fresh progress; remaps/config/history
preserved. The Hard win-rate investigation stays stopped.
SHA256 3453b3fba1a4e3b76c8f859311a499f3f2bf8d9a493280f43ad41eca8b784057; CRC32 A33889B1.


## Current maintenance release — 2026-10-30-r2

Crit Shots locks after the selected season completes match 1; Auto Replays and
other options keep their behavior. Fresh B selects the eligible teammate nearest
the actual puck without skater possession, including own-goalie possession and
old pass animation; Y and own-skater passing retain their rules. A native goalie
outlet goes to that pad's newly selected skater. Stars of the Game keeps its whole
native panel; shot/crowd meters retire before it draws.

The selected 38-win AI base is preserved. All 40 original no-B playbook trajectories
(24,000 frames) match byte-for-byte. New regressions pass; all 67 checks executed:
66 pass and the exact inherited finisher geometry failure remains. Strict QA is
FAILED, as already documented for the chosen playtest. The first draft's new AI
timing regression was rejected; no per-frame guards/padding/RNG forcing remain.
Seven independent build/rollback comparisons and original-base IPS pass.
SHA256 f2a6d2ee40f4ac6c7bfe3277fe1859930fc1c579f1cb15aed742c1798f8fd9a3;
CRC32 AD60AF42. Fresh progress per release; remaps/config/history preserved.
See MAINTENANCE38_R2.md and QA_STATUS.json. The win-rate search stays stopped;
blue-line/one-timer/lateral-deke research is deferred, and human variant tests last.


## Current maintenance priority —2026-10-30

User selected the38-win tactical base for repairs first, then the Downloads
Stars of the Game graphics defect, then other queued work. New attacking
policy (shot-zone/one-timer/lateral goalie deke) recorded as deferred TODO.
Private stage `.workspace/work/maintenance38-20261030/` contains exact public
baseline, complete source and untouched reported screenshot. Crit-lock first
build exposed a short-branch range error; retained, corrected by a long branch
only when the new lock is enabled. No changed ROM delivered yet. AI win-rate
search remains stopped/goal paused. Website headline now explicitly says search
stopped and explains public62/63receiver-position failure; private74QA applies
to the rejected33-win image only. No private passes approve the public38ROM.


## Final Hard test complete — AI search stopped by user,2026-10-30

Final central-combine SHA21d4a4b0c4118054f66bc5811e0a44cdde2930da50ad3e183ba2de8883be0950
passes all74current-source checks,7independent exact rebuild/rollback comparisons,
Native378byte/8veneer/benchmark-source/IPS proof. Fresh primary2881–2930completed
all150: new33/50GF94–62, Native control32/50GF89–65, r3control29/50GF80–69;25homeeach,
no execution errors. All100new/control saves valid;4oldr3scorer errors retained.
No confirmation allocated. Per jdc, STOPAIsearch/use released38fdf88 unchanged.
Stable39not achieved. No new release, installation, save clear or RetroArch closure.
Attribution/time published as Codex GPT-6.1 Sol on High,20+hours perjdc. After next
release TODO Crit-only match1lock then immediate nearest-puck B switching; after
all other work human-test retained variants one by one. Current main testworkers
are terminal; do not restart them or allocate fresh2931inputs. Deferred work is
not implemented. Canonical workspace/headless/noagents policies still apply.


## Final Hard variant and user scheduling —2026-10-30

Codex GPT-6.1 Sol on High, configuration identified by jdc; jdc estimates a little
over20hours of Hard AI attempts/testing. Attribution is user-reported, not a
runtime measurement. Latest strength remains32/50; public38/50 and its62/63failed
QA unchanged. Final central pass/shot variant targeted264caller/112actualNative/
140opening/134choice,14quiet/14central/22retained scenes pass. Full74matrix running;
no new strength pool allocated yet. First marker and comparison-assembly failures
retained; main ROM unchanged. Only this final variant may proceed to fresh primary
and a single qualifying confirmation; otherwise stop AI search/use existing38.
After next release start TODO: Crit-only match1lock, then immediate B switching
when no skater possession/own-goalie possession, preferably nearest eligible
teammate. User reports current intermittent missed/delayed switches. No B/Crit
implementation, new install or RetroArch closure during this test.


## Hard cross-ice passing and shot openings (2026-10-30)

Current private close-slot Hard priority passes14paired scenes,181choices and60
actual Native action/stack checks; all69exact-source checks pass.
Completed150-game primary2831..2880: new32/50, Nativecontrol31, r3baseline25.
Rejected for strength; all100new/control saves valid,6oldr3errors retained.
No confirmation or unchanged-image reseeding; stable39 unfinished.
First68run retained an old test-address marker failure; original76/172/126 cases
pass at actual Native entries, with20additional real caller/precedence checks.
All previous failures remain retained; stable39 and deferred Crit lock unfinished.

Unreleased investigation: a covered-centre Hard carrier can now consider a safe
free wing; controlled early outlets3to8/8,6catches/5following native shots.
The fresh 66-check gate passes after explicit actual retained-catch coverage
was added; the first failed run is retained. The completed 150-game comparison
gave the new image 33/50, goals 101–65, and both controls 31/50. Rejected for
strength; no confirmation or unchanged-image reseeding. Stable 39 is unfinished. See AI_STRENGTH_WIP.md. Public release details follow unchanged.

User-requested playable test release: Edmonton Hard38/50versus Chicago Normal,
goals132–66,25home each; matchedr3control32/50and immediateparent36/50.
Stable39/50plus independent confirmation remains unfinished; all results retained.
Hard now plans accepted native passes, supports real receivers, checks the
keeper opening on the actual catch and selects only more open native post
targets while retaining height/accuracy/power/physics/ratings. Coverage continues
through real passes. Native scorer cache and keeper attacking/defensive counters
are accounted correctly. See AI_TACTICS.md and HARD_STRENGTH_RESULT.json.

All63 exact-ROM/source checks executed:62pass; finisher_hold fails its live
geometry improvement criterion (slot distance1.90to2.78; early releases2to2).
The strict release gate remains FAILED. This exact38image is provided only as
the interim playtest requested by jdc, with that AI regression retained. Independent current-source rebuild, exact
r3rollback, feature ablations and original-base IPS reconstruction pass. Normal/
Easy/human eligibility and physical-shot contracts pass; two Easy live controls
retain sampled frame-timing differences. No physical-controller session claimed.
SHA256 fdf88b7f1933304dd08917b88e1b2e4805072355c1538ff41d876e6e015688c4; CRC32 853481AF.
Fresh progress each release, controller/remap/config/history retained.


## Crit On/Off value visible on every redraw (2026-10-29-r3)

User reported the Crit Shots label without its On/Off value. Native full value
redraw stopped after logical row20, skipping the new row22. Extend F7B44 from
0014 to0016. Both On and Off now render on initial selection, cold boots and
full redraws, in the same column as Auto Replays. The preference already changed
and persisted in r2; those tests missed the actual initial drawing call.

All40 exact-ROM/source checks pass. New native renderer regression reproduces
r2's missing initial value and verifies8 ordinary/adapter cases,32 observations:
default/saved/corrupt values, Left/Right, row changes, exact strings and column.
Existing43 Crit behavior and7 replay cases, plus full natural Hard CPU game,
also pass. Independent rebuild, exact r2 rollback (--legacy-crit-value-draw)
and original-base IPS reconstruction verified. Only4 ROM bytes differ: two
version-header bytes, checksum byte and native draw-bound byte. No AI, power,
controller behavior, save format or gameplay changes. SHA256
0d196f206294ebf6c8bda449a41aa7cf4f6702585a57cc9ecceaee960915a8db;
CRC32 3493DFFC. Evidence: .workspace/work/crit-render-fix-20261029/.
Normal fresh-progress/remap policy applies after normal exit of an older game.
Hard AI35/50 target remains unfinished; no hold/timing/point prototype included.


## Global Crit Shots On/Off (2026-10-29-r2)

The twelfth main-menu row is Crit Shots: On / Off, default On. This global
preference survives cold boots, Full/Light/Mini slot switches and native modes.
It stays editable during a locked season without unlocking match settings.
Off disables the critical roll, extra power, red puck, flame trail and special
sound for CPU and human shots on every difficulty, including Easy's otherwise
guaranteed critical forehands. Auto Replays is saved independently. Ordinary
shots and the separate older long-range goalie-miss rule retain their behavior.

All39 exact-ROM/source checks pass. The new Crit suite covers43 cases: locked
menu edits, cold boots/slot/native-mode persistence, absent/corrupt independent
records, read-only boots,24 direct native CPU/human/team/difficulty On/Off
cases (10,000 shots each, native SFX-entry instrumentation), and six real
controller Off releases including all four Easy pads. Seven strict replay
preference cases still pass. A timing-dependent season-slot fixture now skips
native other-game highlights with ordinary C inputs while keeping the exact
bracket/password/checksum assertions; no gameplay code was changed for it.

Independent rebuild, exact preceding29 rollback with --no-crit-option and
original-v5.1W IPS reconstruction pass; guarded1,311-byte ROM diff. Current
SHA256 fd9f76a4e29dcbd904b898aaba2018ceb1d4f0eff7e8d46f940435c55981b78f,
CRC32 44FEF889. Full evidence: .workspace/work/crit-option-20261029/.
The separate Hard AI35/50 target remains unfinished. No additional AI prototype
is included. Controller mappings retained; progress clears after normal exit
if a preceding user game is still running.


## Canonical workspace — user policy, 2026-10-29

Always work from `/home/jdc/mount/truenas/www/dela.jdc.se/nhl94`.
All contents of the former `~/.config/retroarch/roms/claude` directory have
moved into `.workspace/` under this project, including ROMs, original bases,
backups and the complete `work/` tree. New work uses `.workspace/work/`.
Current continuation: `.workspace/work/hard-strength-20261028-231416/RESTART.md`.
The old directory is a compatibility symlink, not another copy. Keep it for
historical scripts, saved paths and evidence. This overrides all older path
instructions below. `tools/config.sh` derives the paths from the project root.

`.workspace/.htaccess` denies all HTTP access (verified 403). Keep this rule
intact and exclude private work from published archives. Public release files
continue to live in `download/`. Migration evidence is in `.workspace/.migration/`.
The move does not change ROM bytes, version, saves, controller mappings or the
existing exact-source QA receipts. Do not regenerate historical reports.

## Hard CPU coaching, native crash fixes and keeper statistics (2026-10-29)

CPU Hard chooses stronger real keepers and its best first forward unit, with
unchanged player attributes, physics and true fatigue. Normal/Easy AI decisions
retained; human teams protected. Native rewind wrap and powerplay-display team
pointers corrected; reserve/third/multiple keeper season statistics use actual
individual counters. All38 exact-build checks pass, including46 line/46 keeper/
12 accounting/6 rewind/4 PP cases and a full Hard CPU game. Same failing0-2 game
passes with identical goals. Rebuild, exact r9 rollback and original-base IPS
verified. SHA256 b81bbd5510edc8496d9a50cce269dee653067977f6ae05b3ca89cdf3ad20bd8c;
CRC32 B999E29D. See HARD_COACH.md. User requested this playable copy before the
unfinished35/50 strength goal; later experiments and requested Crit toggle
are not included. Fresh progress; controller mappings retained.


**Active strength investigation (not a release):** read [AI_STRENGTH_WIP.md](AI_STRENGTH_WIP.md), then the private HARD_STRENGTH_HANDOFF.md it names. Currentr9 functional passes are not evidence of a win-rate gain.

## Hard pressure, point shots and catch-and-release combinations (2026-10-28-r9)

Nearest CPU skater pursues even a stationary opposing carrier. Clear blue-line
shots are considered more often; wide/point receivers catch and relay, while
free slot receivers can release a buffered native one-timer on the actual catch.
Hard only; native physics/ratings/controls retained. New transient FFF086..08B,
guarded module2DD000, ai_attack and onetimer integration. See AI_PLAYBOOK.md for
exact scope, native RNG timing effects,32 paired gameplay cases and limits.
SHA256 811c912c42d2aacb6a7326c8131090df71972deb63a597d0298b049c05277cea; CRC32 010305B0.


## Hard defensive pair / earlier pressure (2026-10-28-r8)

Nearer back contains the opposing carry goal-side; partner covers the slot.
Hard only, native skating/checking and human controls retained. New ai_gap.s
590 bytes at2DC000, CF8A hook; no persistent RAM or rating/physics boosts.
44 native contract cases and48 paired live scenarios; Hard mean gap -33.33
units (15/16 improve), 24 rushes show11->7 observed attacker releases and7->6
team goals. Small controlled sample, no win-rate claim. See AI_GAP.md.
SHA256 27248d4097dae5f2af9e5f73d02f1811befad2f034a615f2951bf2ff810813c9; CRC32 33EB70DA.


## Shot charge colour progression (2026-10-28-r7)

White at low charge, native orange through the middle, solid red at maximum;
no full-charge blanking. HUD-only shotmeter.s change; original charge/physics,
replay lifetime, crowd bars and palette data retained. Twelve native pad/mode
colour cases and shared exact-build gate documented in SHOT_COLOUR.md.
SHA256 a0cd85710be2004a89a5c0b3d8c97ff1fd77649c79d4d5fe487eaa7831aabd65; CRC32 47DF3530.


**Shared test gate:** Claude and Codex must read TEST_PLAN.md before changes. Use the exact-build release matrix; QA_STATUS.json records the current verified build. No skipped/stale checks count as passes.

## Intermission, ticker, team control and replay preference (2026-10-28-r6)

Scoring Leaders exits with one Start after automatic replay and before period3;
replay's stale no-redraw latch is consumed at its origin and at leaders exit.
Crowd meters no longer erase native penalty ticker cells (old208 damaged pixels,
new0). A resumed solo away fixture keeps the controller on the chosen club,
preserving One-Visitor across locked-settings reload. New main-menu Auto Replays
On/Off is a global saved preference, default On, editable during a locked season;
manual Instant Replay remains available. Sources/addresses: USER_REPORTS_R6.md.

All28 exact-ROM/source checks pass, including32 native identity starts (16 cases
with two cold boots each), eight leaders exit cases, native ticker pixel checks,
seven preference/manual replay cases, four-pad goalies, shootouts, locks and a
complete Hard CPU game. Exact rebuild; guarded5,283-byte r5 diff; original-base
IPS verified. Scripted native inputs; no new manual hardware test claimed.
SHA256 526ae0fd082774a83ca5f74b87b11e638d298f2a672d2a6974fd57d224339c35; CRC32 1599055D.
Fresh progress on jdc/Bodin; remaps retained. Replaces the same current download
and Bodin playlist item. Private evidence: work/intermission-review-20261028/.

## Hard attack AI, dynamic momentum, Start-only credits (2026-10-28-r5)

Hard forwards now support the carrier with slot/return options, avoiding a
marked preferred point. CPU pass decisions scan for a free receiver in a better
position; poor-angle shots are deferred when one exists. Human controls, native
physics and Normal/Easy decision paths retained. Hard now shares Normal's real
crowd momentum/rating effect, removing the permanent ±10% handicap. Easy bonuses
retained. Credits wait indefinitely for a fresh Start press; no other button or
timeout skips them. Sources/addresses/guard limits: AI_ATTACK.md.

76 paired native cases, 16 live attack scenarios (free-slot passes 3/8->8/8,
receptions 2/8->4/8), 12 crowd profiles/84 observations and both credits ports.
All 24 shared checks pass for the exact ROM/source, including keeper/shootouts,
settings locks and full Hard CPU game. Small controlled samples, no win-rate
claim. Exact rebuild; legacy flags reproduce r4; guarded 2,373-byte diff and
original-base IPS verified. r4 backup: private work/ai-attack-20261028/backup-r4.zip.
SHA256 f2b62c979f44da6635faff9ee5cefc28fc0e684da1e7c8ba55c3362a0338b0b7; CRC32 59269517.
Fresh progress, controller remaps retained; current files and same Bodin item.

## Weak-side AI coverage (2026-10-28-r4)

On Hard, a deep, wide opposing carry makes the off-puck AI winger protect the low slot /
most dangerous far-side receiver. Velocity-based short look-ahead, inside and
goal-side target; compact support if none. Both teams/ends; native strong-side,
humans, goalie, ratings, shots and save format unchanged. Hook D086 -> 482-byte
ai_defence.s at 2D8400; no new RAM/RNG. See AI_DEFENCE.md for scope and credits.

44 paired native target/guard/register cases and 19 paired live skating cases
pass (11 Hard improvements and 8 unchanged Normal/Easy controls; Hard mean coverage distance reduction 26.82 units, range 13.14..34.30).
This is not a win-rate claim. All 20 required shared release checks pass for
this exact ROM/source, including four-pad goalie and full CPU game. Rebuild
identical; --no-ai-defence reproduces exact r3; guarded 483-byte diff and
original-base IPS reconstruction verified. Requested r3 backup ZIP with manifest
is private in work/ai-improvement-20261028/; details in AI_DEFENCE.md.
SHA256 e6c727618a252e962e5d120a7f945c25e2634f2a396382c90766386a2fe81c97;
CRC32 975E5D61. Fresh progress, controller remaps preserved; same Bodin item.

## Strict season settings lock (2026-10-28-r3)

jdc: Continue Mini must never reopen the match settings after a reboot.
Release policy updated in the same session: clear game progress on jdc and
Bodin at EVERY new version; keep controller mappings (starts with r3). Removed
the legacy NOSET/open-row exception; saved choices are restored and remain locked
after game 1. New pre-Start T records preserve Auto line changes before native
D058 normalization; old records/snapshots are validated and recovered, without
unlocking rows. Explicit restart permits new choices. Players stays editable.
Old snapshots lack Fighting: deterministic Off fallback, original value asked
of jdc; no claim to reconstruct missing history. See SETTINGS_LOCK.md.

PASS 36 targeted cases/261 observations, actual first-game result -> cold lock,
and a private copy of jdc's Mini; all 16 existing regression suites, including
four-pad goalie, shootouts, menus/slots/HUD/crit. Exact rebuild and guarded diff,
original-base IPS verified. SHA256
c0bb6213131a4ce2daf763a3bfa06b740fe9a81e8dc252b6e2b32b78e0159738;
CRC32 68A92922. Evidence: work/season-settings-lock-20261028/.


## Forum gameplay fixes and boot credits (2026-10-28-r2)

Five forum gameplay corrections: boxed-player pass selection in both scans,
stale human friction mode, penalty-shot late/ghost puck, offensive awareness,
and the worst short-pass speed truncation. Explicit cached-base guards ensure
all fixes enter the experimental build. The CPU goalie pull was already fixed
in v5.1W; no new authorship claimed. New skippable credits card after the first
picture; forum.nhl94.com / Credits navigation, versioned Download labels and
full source attribution. Brodeur30 follow-up / guarded diving-timer tool added.

PASS all 16 final suites: 682 targeted native cases, 80 adapter goalie + 41
ordinary goalie cases (two-frame short taps), 34 OT/shootout results, 14 matches /
14 cold boots, menus/slots/HUD/crit. Old one-frame input and legacy home-dive
fixture limitations reproduced on the baseline and documented, not counted as
passes. Exact source rebuild, guarded 18,950-byte diff, IPS verified; independent
CPU game 1-2 saved correctly. Full details/commands: FORUM_FIXES.md; attribution:
CREDITS.md; review/reply drafts: FORUM_FOLLOWUP.md. No forum messages posted.
SHA256 8caf93b508276b255e274b59573934c6ce7c09804d17bacec99ca93d22090662;
CRC32 14EDE156. Evidence: work/forum-fixes-20261028/.


## Season settings kept; season penalty minutes (2026-10-28)

NHL94 forum report: "create a season with manual goalie, penalties on, line changes on, fight on,
finish the first game, exit the emulator: when you play again your settings are locked on the
wrong ones" and "I don't see PIMs being saved". Cause 1: Per.Length $D050, Goalie Ctrl $D052,
Penalties $D056, Line Changes $D058 (and $D072) and Fighting $F004 lived only in RAM; power-on
defaults reset them and the season locks those rows after game 1 (r22). seasonplayers.s
diffstore (each new game, season view) now also writes a record at logical SRAM $1FF6..$1FFC
('S', the five values, xor check), unused in every bank; load (boot / slot switch) restores
them. Seasons without the record (saved before this version) get NOSET $FFF0DC = $5345 and
their rows 8-16 stay editable (seasonmenu.s calls seasonplayers +56 openrow) until the next
game records them. Tested: change all five, start game 1, reboot -> restored (record
53 02 01 01 01 01 58); jdc's own save: rows open (forced 1, Penalties changed).
Cause 2: season stats held G/A/SOG only. pim.s ($2EE000): after every played season game the
team blocks' per-player minutes (+$102 + roster slot, bytes) are added (saturating) to logical
SRAM $09A0 + 25*team (league, 800 bytes, unused) or $07C0 + 25*SEEDS slot (playoffs; postats.s
clear now runs to $096A), keeping the checksum with season.s DELTA; a new season zeroes the
league block (season.s clearall). Simulated games add none (the sim has no penalty model).
seasonend.s: a PM column on the club pages (league / playoffs / both by tab). Tested with
injected 4+5 and 2 minutes: blocks 9 and 30 correct, checksum valid, shown on the page.
PASS critical 10, critical_flash, season_navigation 18/342, finished_season 9/108, menu_team
18/240, menu_font, season_slots 20, overtime/shootout/playoffs 18. SHA256
02fccc36c6d4a8002aaf2b4c1e028a4912cf3c7aab9ac80297cb02fff1882c86, CRC 687C11B1.

## Restart a finished season from the main menu; bar fixes (2026-10-27)

jdc: after a finished season the main menu only offered "See Old Full Season"; a new one had to
be started from inside it. Play Mode of a finished slot now has two steps: See Old ... (as
before) and, one step Right, "Restart Full/Light/Mini Season" (20-character labels: the menu
only draws the new text, longer labels left their tail behind, seen with a 22-character try).
Right again goes on to the next slot, Left steps back; arriving from the right lands on Restart.
Start on Restart goes straight to the finished.s confirmation (X + B = restart that slot only,
Start = cancel back to the main menu). Code: finished.s +28 step / +32 arrive and the label
switch (NEWPICK $FFE3FE = $4E57, menu RAM, cleared on every slot change and on use);
seasonmenu.s only calls them (it is at its 2 KB limit). Tested with the finished-season seed:
Right -> Restart label, Start -> confirm, cancel -> menu, Left -> See Old, A+C -> "New Full
Season 1/84" and the slot header reset (SESN 0,0,0,84,1,3).
Also: the shot bar had transparent top/bottom rows (from the sprite era) inside its black panel:
mkbar d3 bit1 = solid rows. The crowd-bar tiles moved from $7D8 (VRAM $FB00, window rows 22-23)
to $7E5 ($FCA0, the old crowd-tile area): jdc saw ice/boards garble in the corner after a glass
broke - the window plane is probably used there. Not reproduced headlessly (no way to force a
glass break found); the move returns to the VRAM footprint used safely for many releases.
PASS critical 10, critical_flash, critical_trail 1422/1422, season_navigation 18/342,
finished_season 9/108, menu_team 18/240, menu_font, season_slots 20. SHA256
f72c395640b42496e765696dd24d4b996fb3bf27a0b628c51afd0b30eab6de9d, CRC D7FF22ED.

## Meters in the HUD plane; flames after season menus (2026-10-26-r37/r38)

r37 (jdc: flames gone after a replay and in the game afterwards; red puck and sound fine,
EDM-COL): reproduced only from jdc's real season save: at the first frame of play trail.s RAM
held season-menu text (HN 32768, HT 3348, TL 32025). HT counts down only while a crit is
active and uploads the flame tiles at -1, so they stayed blank for ~3300 frames of crit time.
crowd.s valid (new game) now clears $FFF630..$FFF637 and trail.s resets TL > 24, HT > 31,
HN > 9. Forced-crit comparison in jdc's season: r36 no flames, r37 flames.
r38 (jdc: skaters skate OVER the crowd bars and the shot bar): sprites of ours lose to every
earlier native sprite. Bars are HUD-plane ($E000) cells now, in front of the players like the
score box: crowd.s +36 mkbar composes a 32x8 bar (rows 0/7 transparent, 1/6 background, 2-5
fill x 1..fill, optional neutral tick at x 16) into 4 tiles; white = palette 0 (1 on 4), red =
palette 1 (A on 7, tick 1). Crowd bars: tiles $7D8..$7DF (VRAM $FB00, window rows 22-23, never
shown; REPLAY borrows $7E1..) at rows 24 (away) / 26 (home), columns 27-30; erased once in
replays / other views (BSHOWN $FFE8FE). Shot bar: tiles $7E1..$7E4 in Codex's panel row 19,
columns 34-37 (replay keeps the dash glyphs). Buffers $FFE900 (3 x 128), keys $FFE8F0: tiles
are composed and DMA'd only when a fill/colour changes (composing every frame cost enough CPU
that verify_critical_trail saw lag frames: 1399/1491; with keys 1422/1422). The 27 static
crowd sprite tiles are no longer uploaded; sprites drawn by crowd/shotmeter: none.
PASS critical 10, critical_flash, critical_trail 1422/1422, season_navigation 18/342,
finished_season 9/108, menu_team 18/240, menu_font. SHA256
b00475a53ed761424d618612debd05c0d7e57dd5a5ab0e4a36ee560346ccbf50, CRC CCA479E8.

## Difficulty saved per season (2026-10-26-r36)

jdc: Continue Mini showed Difficulty Normal although the season was played on Easy. DIFF
($FFF07A) was RAM only and crowd.s's power-on defaults clear it; since r22 it locks after the
first season game, so it could not be set back. seasonplayers.s: new optional record in the
unused logical SRAM $1EF4..$1EF5 (physical $203DE9/$203DEB: difficulty, ~difficulty), right
after Codex's PC count record $1EF0..$1EF3, inside the 8 KB bank the slot manager snapshots.
load (slot init at boot / slot switch, mapper on) restores DIFF from a valid record; legacy
slots without it keep the current value. +52 diffstore, called by crowd.s valid (new game, or
Team Roster), writes DIFF in the season view only when it differs, then recomputes the native
checksum (the same tail as store). jdc's save was migrated by a real r36 game start with Easy:
4 bytes differ from r35 (record + checksum); the r35 save is kept in
work/difficulty-save-20261026/. Measured: Easy stored, reboot -> DIFF 2. PASS season_slots 20,
critical 10, season_navigation 18/342, finished_season 9/108, menu_team 18/240, menu_font.
SHA256 7c372516c2e24ea57f54b9926877b27de8360ce76d2d72504e6e25a4c1514125, CRC 2DEF2B52.

## Faster live Scoring Leaders (2026-10-26-r35)

jdc: after looking at the Scoring Leaders, Start had to be pressed twice (a black screen
otherwise). Not an exit bug: one Start always returned (tested after a real season game from
jdc's save, both pad types, every button, sorts/pages). The r34 ranking scanned all 800
players 32 times with a scoring subroutine and a bitmap test each time: about 60 frames
after opening (and after every C sort) with no input read, so a Start press then was lost.
leaders.s rank is now one pass: each player scored once, inserted into a sorted top 32
(scores in STR, free until draw; quick reject below the 32nd; strict > keeps the first found
on ties, as before). draw's G/A temp moved from KEYS to TAKEN, so a sort re-ranks without
re-reading SRAM. Same LORD bytes as r34 for PTS/G/A on the synthetic 800-player season and on
jdc's save. List visible ~20 frames sooner; the remaining ~30 frames are the native fade.
PASS critical 10, season_navigation 18/342, finished_season 9/108, menu_team 18/240,
menu_font. SHA256 c9b227db1c9696ed78bf20b89a112a23f9a6f4ee933310b625ec9ed1f91277f6, CRC C6899E68.

## Sortable live Scoring Leaders (2026-10-26-r34)

jdc: after a game the season's Scoring Leaders (pre-game menu) could not be sorted by goals /
assists like the finished-season browser. Codex (tools/season/leaders.s): C (Xbox B) cycles
PTS -> G -> A (one step per new press), raw G/A words with an 800-bit selection bitmap
(TAKEN $FFEA40; zero and maximum stats rank correctly), LONG sort scores (points may exceed
255; ties: points/goals, goals/points, assists/points), selected heading in palette 3 (a copy
of the font palette 2 with red, restored before the native exit), footer "Sort: Xbox B /
Mega Drive C". RAM moved: LORD $FFEAA4, PAGE $FFEB24, SORT $FFEB26, HDRB $FFEB28, STR $FFEB40
(ends before $F000), PALCOPY $FFF180. Claude finished it: the in-place redraw left the ends of
longer names ("Jeff Malottovace"), so names are padded to 16 characters. Probe (synthetic
season, 800 players): PTS/G/A orders, Right/Left paging, Start back to the menu, the menu
reopens normally. PASS critical 10, season_navigation 18/342, finished_season 9/108,
menu_team 18/240, menu_font. SHA256
a8b5515ac0b03de4cea1802c8707b0468ffcdff8615d54e70b311ccdc155f2c1, CRC 6E810C21.

## Crit flames follow the moving camera (2026-10-26-r33)

Reproduced Mini/Easy/5min/Fighting Off,EDM-CHI: red puck+bomb sound but
the six flame circles overlapped because HIST stored old screen coordinates.
trail.s now stores camera-independent anchors and projects at draw time;
other views normalize their native puck sprite.20 native crit shots show
median span11->42px;1424/1424 world-path checks pass(previous0/1424).
Same art,VRAM,RAM,6 sprites,80-sprite cap and24-frame afterglow. Crit physics
and goalie bonus unchanged. Critical10,velocity88,flash,panel8/161,navigation
12/228 and full CPU game pass. Exact rebuild,guarded115-byte UI/title/checksum
diff,IPS verified. See [CRITICAL_TRAIL.md](CRITICAL_TRAIL.md).
SHA25607c63ff6583b8edd235f7b2cf22b4584ace23174c7ba1009385a37b115ea62e9;
CRC7BB0CADE. Replaces r32 with save/remap hashes preserved.

## Strong crits and shot balance audit (2026-10-26-r32)

Fixed signed-word overflow above release power481: the strongest crits could
reverse sideways and trigger the native slow-Y fallback. New90-byte
critical_velocity.s at2ED800 uses wide math and signed-component saturation;
low powers retain the native path. Paired14,400 controller shots,3 clubs,all
3 difficulties/4 period lengths: lower-power outcomes unchanged. Requested
Bouchard Easy run32/100; goalie bonus541/3600(15.03%),0/3600 below 160;
Easy2400/2400 actual crits. Matched1,000-shot controls248goals production,
187without goalie bonus,161without crit power. Native boundary88/88(fixes32
previous failures),crit/flash/panel/navigation/menu and full CPU game pass.
Exact rebuild; guarded91-byte diff; IPS verified. See [SHOT_BALANCE_AUDIT.md](SHOT_BALANCE_AUDIT.md).
SHA2568a7304d8fd5708907dced70ea016fffaa8f3096389be18c809f0ebc086423fd9;
CRC92065481. Replaces r31 with saves/remaps preserved.

## Shot panel retained through replay, above the score box (2026-10-26-r31)

The64x40px shot panel now matches the score box and sits immediately above it,
with the charge bar inside. Recorded km/h/mph and charge stay visible throughout
the automatic replay and disappear on return (also on skip). Live bar remains
smooth/red; replay uses native HUD segments so REPLAY keeps its borrowed tiles.
No new pattern memory, palette or DMA. UI full-charge point/sampling now matches
native31; physics/crit/sound and all other game modules remain unchanged.
PASS8 native cases/161 VRAM checks,critical10,flash,navigation14/266,finished9/108,
menu18/240;80 standard goalie cases pass, old home-dive capture fails equally on
r30/r31. Rebuild exact,guarded1099-byte diff,IPS verified. See [SHOT_PANEL.md](SHOT_PANEL.md).
SHA2568e45bb86e1728c5933bbe4614a6a89007459b0a88839570f4415b90cde8c48da;
CRC1A35E68D. Replaces r30 everywhere with save/remap hashes preserved.

## No goal replay after the final horn (2026-10-26-r30)

jdc: a replay played when the game was finished. Reproduced headlessly (period 3, 0:05,
2-1, no goal): at the horn replay.s armed its 90-frame countdown. A debug build saved the
values at arming: goals 3, LASTG ($FFF73E) 0 - the end-of-game code zeroes that RAM, so the
unchanged score looked like three new goals. replay.s now stores the goal sum tagged ($A5xx);
an untagged LASTG (zeroed by the game, or crowd.s's clear at game start) only resyncs, it
never arms. Measured: no replay after the horn (regulation and tie/OT runs); the goal replay
still plays. Also checked jdc's Easy report (18 blue-line shots, no goal): a human forehand
slapshot does crit in Easy (red puck, release power 404 -> 491 in the fixture); a crit is
+25 % power, not a guaranteed goal, so the goalie can still save it. PASS critical 10,
season_navigation 18/342, finished_season 9/108, menu_team 18/240. SHA256
a01aa1ef581199af2a50256613c2470cc9ca3329ef8a3117b3fdef1c6776b556, CRC DA8E5281.

## Shot speed as a score-box panel (2026-10-26-r29)

jdc: make the km/h / mph readout a box like the EDM 1 / STL 0 score box. The score box lives in
the fixed HUD plane ($E000, 64 cells wide, transparent $07FF elsewhere; rows 23-27, cols 32-39)
and uses resident tiles: frame $3FF-$407 (palette 1 + priority, $A3FF..), black fill $403, font
palette 0 + priority: '/' $41D, digits $41E+d, H $435, K $438, M $43A, P $43C. shotmeter.s now
writes a 10x4 panel of those map words at rows 8-11, cols 30-39 (top right, under the bar) on
release (" 165 KM/H", " 102 MPH ", leading blanks = $A403), with interrupts off and VDP
auto-increment 2; it writes $07FF back when SHOW runs out (240 frames), at a new wind-up, or on
the first replay/other-view frame (PANEL $FFF64C). The narrow r28 font, the $FB00 text tiles,
the per-frame DMA and the text sprites are gone. Measured: panel at the stands and the rink,
gone in the goal replay. PASS critical 10, critical_flash, season_navigation 18/342,
finished_season 9/108, menu_team 18/240. SHA256
f641c2c7ae671728c4ea3068efb145dcac7b8d2050424099a4a258130b99d7a0, CRC 1CDF67B2.

## Shot meter lower and stable; replay hint in score-box style (2026-10-26-r28)

jdc: the goal banner (top) covered the speed text; after a broken glass the text was garbage;
and the replay's A+C hint should look like the score box (EDM 1 / STL 0). Cause of the
garbage: the glass shards load into tiles $3EF.. (the r27 text). VRAM check over 80 saved
states: window rows 14-21 ($F700-$FAFF) hold game tile data, rows 22-23 ($FB00-$FBFF, tiles
$7D8-$7DF) are never shown and empty in all of them. shotmeter.s now composes the two lines
with a 3x5 font, two characters per tile (8 tiles, shotmeter.py), in RAM $FFE900 (statistics
browser RAM, free in play; glyph indices at +$100) and queues a DMA of the 256 bytes to $FB00
every frame while shown (a5 queue in the crowd.s frame), so even a borrowed tile comes back
at once; each line on crowd.s's black 32x8 plate; bar at y 56 (below the GOAL / Face Off
banners). Replay hint: replaytitle.py draws "X+B SKIP" pixel by pixel in the score-box style
(56x16: frame palette 0 5/6, black plate, 6x7 blocky letters colour 3). Measured: text
steady at the stands; hint shown in the replay. PASS critical 10, critical_flash,
season_navigation 18/342, finished_season 9/108, menu_team 18/240, replay 23/24 distinct;
four_goalie 080-dive remains the r27 fixture artifact. SHA256
09b4508be92d9c650de53166615975d4694962ebd9152c3024ea361aa312cf3f, CRC DA83A102.

## Shot power meter with km/h / mph; Difficulty Easy (2026-10-26-r27)

jdc: a small meter top right for the slapshot charge (full = red blink = shoot), the shot
speed under it in mph and km/h, honest rink scale, crits may beat the record; and a third
difficulty, Easy: meters always full and 100 % crits on (forehand) slapshots.
Charge: native windup animation $7FC / $92E, counter $5A (< $10 charging), BEDE 15 -> 34
(+~1 per frame, full after ~18 frames); release power = charge x (shot power rating + 20) x
0.32 (rating $6C through $15730 = x energy). Measured releases: hold 6 -> 236 (~95 km/h), 12
-> 326, full -> 382 (148 km/h); backhand 303; crit +25 %. Speed: |($B772,$B774)| / 3842 = rink
units per frame (checked against positions); centre line - goal line 262 units = 89 ft =
27.13 m, 60 frames/s: km/h = V x 763 / 2^17, mph = V x 237 / 2^16. Hardest normal shot
(power 30, full energy, full charge, forehand) 148-153 km/h / 92-95 mph; crit 176-184 km/h /
109-114 mph vs Chara's 108.8 mph (2012 NHL All-Star Skills, Guinness).
shotmeter.s $2ED000: +0 logic (crowd.s frame): the pads' own players (C320.., sides C328..)
winding up with the puck -> SHOOT/CHG/LIVE; on the loose puck with BED8 = shooter: integer
sqrt, two 8-tile text lines written to tiles $3EF.. (empty in play at every camera position;
$3BC is used by the crowd graphics near the stands), SHOW 240 frames. +4 draw (trail.s
end-of-list hook, 80-sprite limit): bar at x 280 y 8 with crowd.s bar tiles (white; full:
red, blinking only while winding up), two 4x1 text sprite pairs under it. RAM $FFF640..
(magic 'SM', cleared by crowd.s gameend). Font: shotmeter.py (5x6 glyphs, outline).
Easy: menu row 9 values Normal / Hard / Easy (DIFF 0/1/2; count 2 -> 3 at $2095AE/$2095C2
+18, text table $209700). crowd.s frame: DIFF 2 mirrors Hard (humans +10 % full bar, CPU -10
% empty); critroll: Easy + human -> threshold 1000; new +32 easylookup replaces bsr.w lookup /
tst.w d0 at $2FF45A so a human with Crit 0 still reaches the roll. Measured: shooter with
Crit 0: Normal 382 (no crit), Easy 477 (crit, red puck); Easy bars full for WAS, empty CPU.
PASS critical 10, critical_flash, season_navigation 18/342, finished_season 9/108,
menu_team 18/240, menu_font. verify_four_goalie 080-dive-g5-p1-n4 ('no native catch')
fails only with the meter's end-of-list sprites present (a no-draw variant passes all 88;
the meter does not write game RAM): a cycle-sensitive dive fixture, like the r23 ones.
SHA256 109cfa631878ea9435d641b6e33f61cd42622c30f5511ba42a269d68242d73b7, CRC 54E6F043.

## +3 crit points; co-op passes reach a human teammate within +-90 deg (2026-10-26-r26)

jdc: crit chance +3 % overall, and better passes between the human teammates.
Crit: crowd.s critroll adds 30 to the RNG(1000) threshold after the meter scaling (crit
rating 0 still never crits: critical.s skips the roll). Measured 10000 shots: rating 100
1302 (13.0 %), 50 818 (8.2 %), 0: 0. Team Roster shows the plain rating (critshow
unchanged). verify_critical expects (rating + 30)/1000.
Passes: the native scan ($BCF8) only considers teammates within one direction octant of the
pass direction ($BEDC; cmp.l #$10000 on the squared octant difference at $BD3A); the base
hack scorer $2FF000 (and its fallback $2FF180) then give a human receiver score 0. A buddy
a bit off the aim lost to a CPU skater straight ahead. passcone.s (base hack free bytes
$2FF02A) replaces the compare: two octants (+-90 deg, $40000) when passer and candidate are
both human. $2FF01C/$2FF1E2 moveq #0,d2 -> lsr.l #8,d2: humans stay far ahead of CPU
skaters, the nearest human wins among several. Staged test (P1 with the puck holding
Right, CPU teammate straight right, P2 at ~70 deg): buddy above: r25 and r26 aim P2; buddy
below: r25 aims the CPU skater, r26 aims and delivers to P2. PASS four_goalie 88, critical
10, critical_flash, season_navigation 18/342, finished_season 9/108, menu_team 18/240
(goalie_request clear-11-p2only remains the r23 timing artifact). SHA256
030d4dca53de8aa07f428a713f7dbfbfede7c5257d196649a8b7104ee620d951, CRC FDD66E23.

## Replay: "X+B SKIP" hint and no Options box on exit (2026-10-26-r25)

jdc: the replay's own menu (B Options / C Reverse Angle / START = RETURN TO GAME, dimmed
screen) flashed after REPLAY; and a smaller skip hint in the same font. Exit: replay.s state
$A503 used Start twice (first opens that menu, $A33E -> $A4A8, second leaves); now it sets
$C2F6 bit0 (menu open) before the single Start, so the native loop takes $A358 -> $A39E and
leaves at once, also for the A + C skip. Hint: replaytitle.py renders "X+B SKIP" 56x16
(7x2 tiles, same font and chrome bands) into the box's own 14 tiles (B026, reloaded from
ROM by every replay); layout REPLAY rows 0-2, hint rows 3-4, columns 2-8 (+ two cleared
box cells), all inside the rows 0-4 the game redraws on exit. Measured: replay snapshots
show both; the end and an A + C skip fade straight to the game (no box). PASS four_goalie
88, critical 10, critical_flash, season_navigation 18/342, finished_season 9/108,
menu_team 18/240. SHA256 9f6eb8c1e2081702b5b5815c315874d6a276ff90838d8d09f81433f0ef728294,
CRC F16CC862.

## Four pads: Y takes the goalie while a teammate skater has the puck (2026-10-26-r24)

jdc: in 4-player co-op Y cycled skaters instead of taking the goalie when he had no puck
(B from the goalie back to a skater worked). fourgoalie.s reader/handler treated the WHOLE
team's possession as "Y = B" (yoldb/yasb, handler no_y); with r22's fast B switch that B
switched skaters. Now Y = B only when this pad's player carries the puck or the pad's own
goalie holds it (Codex's ypass outlet); a teammate skater's puck, the opponent's or a loose
puck: Y takes the keeper (reader ywant; handler compares B7AA with D6 and the same-side
goalie). Measured (4 pads, Y then B per pad): r23 Y switched skaters with a teammate's puck;
r24 Y -> goalie (teammate/opponent puck), B -> skater, Y with own puck = pass. PASS
four_goalie 88 (incl. 8 ypass), critical 10, critical_flash, season_navigation 18/342,
finished_season 9/108, menu_team 18/240; the two r23 fixed-frame artifacts remain
(season_slots full84 playoff game, goalie_request clear-11-p2only). SHA256
1825b5e48711e287025989c2b9c95323a2d899dd18d88e08242a577257627659, CRC C71CE1C9.

## Big "REPLAY" title on the automatic replay (2026-10-26-r23)

jdc: the grey box with letters (native VCR controls) should be a big REPLAY in the NHL'94
logo style. The box is window-plane art ($F000, 64 cells: rows 2-4, columns 2-9, tiles
$178..$185 = B026, palette 0, priority) that $9FD0 loads from $BB4F6 at every replay start
and the game redraws (its whole panel) on exit. New replaytitle.s at $2EC000 (code + 27
tiles generated by replaytitle.py: Open Sans Condensed ExtraBold Italic, 72x24, 1 px black
outline, silver bands in in-game palette 0 greys 1/3/5/6, black 4), called by replay.s on
every playing frame (ARST $A502): first frame copies the tiles to the crowd meters' VRAM
($FC20, tile $7E1); every frame writes window rows 1-3, columns 2-10 (priority, palette 0)
and clears the box shadow row 4. crowd.s `inreplay` (ARST $A502..$A506) skips the meter
sprites and their tile re-upload; replay.s clears TICK after $9FD0 so the meter tiles come
back next frame. The pause-menu Instant Replay is unchanged. Measured: REPLAY on every
replay snapshot, meters hidden during and back after it, HUD normal.
Regressions: PASS four_goalie 88, critical 10, critical_flash, season_navigation 18/342,
finished_season 9/108, menu_team 18/240. Two fixed-frame fixtures shift with the extra
crowd.s cycles (proved with a variant whose inreplay never reports a replay: same result):
verify_season_slots full84-final-and-playoff-game (the poked 2-0 at frame 9000 now gets its
automatic replay before the final whistle, +1000 frames; with later Start presses the result
and password are recorded, 8 bracket wins) and verify_goalie_request clear-11-p2only (the
trace samples the transient pass descriptor $F058 inside the pass frame 111; same pass, same
receiver at 184, controls unchanged). SHA256
c00fd040d566ee13d1720ade44bafe668cdd8d7d720a774422c2482b6be69ebd, CRC 986E7996.

## Four-pad B switch; Fighting / Difficulty locked in a season (2026-10-26-r22)

jdc: Difficulty and Fighting could be changed after a season game; with four teammates B
did not switch to the free skater, nor from a Y-selected goalie. Lock: r18 had left rows
>= 8 (Fighting d7 = 16, Difficulty 18; Line Changes is 14) open after the freeze whose real
cause was the slot restore clearing D046 (fixed in r18 slots.s); r15 had left Difficulty
open on purpose. seasonmenu writehook now applies the normal FORCED rules to every row:
editable before the first game, locked once the season runs, in season playoffs (Players
excepted, r20) and completed seasons. Checked: mid-season Right on Difficulty/Fighting
keeps Normal/Off with and without the adapter, menu responsive (Play Mode 0 -> 2), two
full adapter Play Mode cycles; before the first game Difficulty still changes.
Switch: with the adapter and co-op sides fourgoalie.s early sent B to the native handler
(nothing for a non-carrier; the native held-B switch is off in co-op so Y takes the
keeper). New `fast` in fourgoalie.s early, after the outlet request and the own-goalie
pass-in-flight check: on a new B press when this pad's player is not the carrier and no
pass is under way, choose the nearest (puck + velocity, as switch.s) skater of the bank's
side that is not a goalie, not human, not down/unavailable, assign with C0BC/C0DA (bank
offset 0/2; P3/P4 copied back by the native swap), set BHELD for the physical pad, rts.
Measured (4 pads, 12 B presses): r21 1 switch; r22 9 switches, the 3 others are the
carrier passing (2) and an outlet request while the goalie held the puck (1). Pad 1 as
human goalie: B -> skater 3 (r21 stays goalie). PASS four_goalie 88, goalie_request 41,
season_slots 20, season_navigation 18/342, finished_season 9/108, menu_team 18/240,
menu_font, critical_flash. verify_four_lifecycle fails at slot1-pads3-fast-reboot on r21
too (side check after reboot; predates r22, likely the r20 per-season Players change).
SHA256 3f5aa9a5c867519b7ededd4d3ddfcfdc05ec9f4c14a8a3c830ba1d35575ac9aa, CRC 9F8518B5.

## Three/four in original playoffs (2026-10-26-r21)

Original BO1/BO7 and Continue Playoffs now offer Three/Four - Teammates, save
that choice separately from the seasons and retain Two - Head to Head.
PASS10 native result cases/18 cold resumes, independent Full1/Light2/Mini3/
native4 roundtrip/reboot, Continue player edits with unchanged password;
season24/48starts,slots20,nav14/266,font14/3124,goalie88+41 and3 four-pad
season shootout results. Exact rebuild, guarded1691-byte diff, IPS verified.
Source/storage/tests: [NATIVE_PLAYOFF_PLAYERS.md](NATIVE_PLAYOFF_PLAYERS.md).
Replaces r20 everywhere with saves/remaps carried unchanged.
SHA25637feaa90d54806517026d26e67e5fb77ff4992408f3ce26c458f2302f0c38759;
CRCADE85330.

## Saved player counts, including Continue Playoffs (2026-10-26-r20)

Players is now reachable and editable in a season's Continue Playoffs menu.
Choose One/Two/Three/Four - Teammates; each Full/Light/Mini slot remembers its
own count across slot changes and cold boots, in both league and playoffs.
The optional PC/count/complement record at logical SRAM1EF0..1EF3 follows
existing snapshots/checksums; old saves remain compatible. Without an adapter,
a saved Three/Four runs as Two and returns when the adapter is restored.

New seasonplayers.s at2EB000 (622bytes), cursor hookF7782, seasonmenu2002 and
slots1556. Only Players is unlocked; club/results/tree/statistics stay intact.
PASS24 native player-choice cases/48 cold match starts, mixed Full4/Light2/
Mini3 roundtrip and reboot,18 played playoff OT/shootout result/save cases,
goalie88+41,slots20,navigation14/266,finished9/108,club labels18/240,font14
cases/3124 calls/zero wrong fonts. Exact rebuild, guarded3579-byte diff,
original-base IPS230941 bytes verified. See [SEASON_PLAYERS.md](SEASON_PLAYERS.md)
for source, RAM, storage and reproduction. Replaces r19 everywhere with matching
save/state/remap contents preserved.
SHA256a67ecaffe77beb7c13071caee9f507767a4efb7471af3c9ff9487d3b3fb9ae3e;
CRC086E5C8E.

## Four-pad goalie hold, B request and Y control (2026-10-26-r19)

The goalie patch now handles all four physical pads in cooperative 4-Way Play:
AI holds on pickup, B requests a pass to the requester, Y explicitly takes the
keeper. Native P3/P4 reuse D4=0/2 with temporary control banks; requests now
retain physical IDs 0/2/4/6. Short hardware B taps survive the catch/recovery;
first valid request wins, and pause/replay input cannot queue an outlet.
Also fixed: switching from Four into a One-player season could still start
four humans (stale TM), and shootouts left pads 3/4 active with only two actors.
Ordinary two-pad switch/Y code is byte-identical. No SRAM format change.

PASS 88 adapter goalie +41 ordinary goalie cases; 7 two-match lifecycles
(14 games, 14 cold boots, P3/P4 movement, pause/resume, menu circles); a complete
natural two-game Mini run produced 27 keeper passes/17 receptions. PASS 50
OT/shootout cases across ordinary/three/four pads and all playoff formats;
slots20, adapter navigation14/266, completed-season9/108, club labels18/240,
menu font. Exact rebuild, guarded2870-byte diff, original-base IPS verified.
Full source/RAM/test details and the known RAM-only playoff player-count
limitation: [FOUR_PAD_GOALIE.md](FOUR_PAD_GOALIE.md).
User-requested private r18 ZIP backup and hash manifest verified. r19 replaces
r18 locally/site/Bodin's same playlist item, carrying save/state/remap hashes.
SHA2567262f8e4a39d6970b660e24803f1d7212b29aed26a933202296b788aed8d9ec6;
CRC D82B8F78.

## Main menu froze with 4-Way Play when switching Play Mode (2026-10-26-r18)

jdc: with both ports on 4-Way Play, browsing Play Mode froze the menu. Reproduced on his
save: Mini -> New Playoffs -> /7 -> Shootout -> See Old Full Season, then no input at all
(cards still animate). Cause: slots.s restore reads the slot's $D046..$D087 snapshot, and
$D046 is the 4-Way Play hardware flag; the Full slot was saved without the adapter, so
D046 became 0 and the menu read the pads the non-multitap way (no input), while the menu
rewrote rows 8/10 every frame (writehook ~3 calls per frame; a locked slot blocked the
game's correction of a saved Line Changes Auto). Fix: restore keeps the live D046 around
the read; seasonmenu writehook never locks rows >= 8 (Line Changes, Difficulty, row 10)
(replaces the Difficulty-only exception). Measured: two full Play Mode cycles respond
(Mini, Playoffs, /7, Shootout, See Old Full, New Light, Mini ...). Diagnosis with private
debug builds (writehook/restore counters at $FFF0FC/$FFF0FE, not released). PASS
season_slots 20, goalie_request 41, season_navigation 18/342, finished_season 9/108,
menu_team 18/240, menu_font, critical_flash. SHA256
8e0b1468026afea2401e31faa9e181844684f860c6d55bcbf3b8af3d4f661f5c, CRC B1E8E59E.

## Three / Four - Teammates: the season's first game kept no team (2026-10-26-r17)

jdc (r16, 4 players): after the first match the main menu hung and said Regular Season.
Reproduced on his save: season.s starts a new season from the first game's Players value
and knew only 1 / 3 (home) and 2 (away); 5 / 6 went to "just record", leaving the header
(1, 0, 0, 0, 0, 3): no team, length 0 -> the menu showed New Season Mini 1/84 and the
season state was broken. Now Players 0 / 4 record only, 2 = away team, everything else
(1, 3, 5, 6) the home team (shorter code). slots.s set-up kept only 3 (and 6 -> 3, the
playoff-state Teammates), so Three became One: Players 5 / 6 now stay when mtap TM is 3 / 4.
Measured: 4-player game won 3-0 -> header (1, 31, 1, 8, 0, 3), reboot shows Continue Mini
2/8 at PIT (Players back to One: TM is RAM only). Also: the game detects 4-Way Play only
when PORT 2 is set to it (port 1 alone: D046 = 0), and only at power-on/Restart.
test_core.py: GPGX_DEV2 sets port 2 separately. PASS season_slots 20, goalie_request 41,
season_navigation 18/342, finished_season 9/108, menu_team 18/240, menu_font,
critical_flash. SHA256 5d69777a2bf7c37bd2cced37f9442b396709dbd9b34fe5240f501828ebce78ef,
CRC 9CF8708E.

## Three / Four - Teammates with 4-Way Play (2026-10-26-r16)

jdc: three, then four players on the same team. Native (D046 = 4-Way Play): Regular Season
Players 1 One-Home, 2 One-Visitor, 3 Two-Teammates, 4 Two-Head to Head, 5 Three (pads 1+3
v 2), 6 Four (1+3 v 2+4), 0 Demo; playoff modes only One ($0B) / Head to Head (7) /
Teammates (8). Side assignment $17E42 (clears C328..C32E, tables $17F52/$17F72/$17FE6,
exit $17FE0). New mtap.s at $2EA400: +0 hook $17FE0 (jmp): with D046 and TM ($FFF07C; or
Players 5/6 in Regular Season) >= 3, pads 2..4 take pad 1's side (Four: all four; Three:
pad 4 none); +4 coop (Z for Players 3/5/6) replaces seasonmenu's five cmpi.w #3,PLAYERS
(same 6 bytes); +8 Players row: One / Two - Teammates (/ Three / Four with D046), sets TM.
Strings "Three"/"Four" -> "Three - Teammates"/"Four - Teammates" (F7EA4/F7EFC/2093A0/2093F8,
F7EBA/F7F12/2093B6/20940E). crowd.s defaults clears TM with DIFF (clr.l $FFF07A).
Measured (GPGX_DEV=1281): row cycles One/Two/Three/Four - Teammates, Game "(co-op)"; in the
game sides 1/1/1/1, pads 3 and 4 move their own skaters (Right held: x 9 -> 118, -68 ->
114). Native behaviour remains with D046: switch.s / outlet.s / goalie_ai.s keep native
paths (pad 1 takes the goalie when he has the puck, Manual); occasionally pad 2 picks pad
4's skater for one frame (native auto-select). TM is RAM only: after a power cycle in the
season's playoffs, Four falls back to two humans until Players is set again (row locked
there). RetroArch: port 1 and 2 device "MD Joypad 6 Button + 4-WayPlay" (1281) only when
playing 3-4; the jdc remap stays 513. PASS goalie_request 41, season_navigation 18/342,
finished_season 9/108, menu_team 18/240, menu_font, critical 10, critical_flash. SHA256
ed854658d2d224951a9cfd6efd3ea0b3ed99c79ebff0d1f74fbb3c83791a9b17, CRC 017DE68C.

## Main menu picture stays grey; menu music back (2026-10-26-r15)

jdc: the r14 menu picture changed colour (orange with Philadelphia away) and the menu music
had been missing for a long time. Palette 2 is the away logo's palette (palette 1 the home
logo's); the r14 check happened with Los Angeles (black/grey) away. Checked 10 team pairs:
palettes 0 and 3 never change in the menu. menubg.s now maps the intro's levels onto palette
0's greys (000=4, 049=14, 092=7, 0DB=6, 16D=5, 1B6=3, 1FF=1; the missing $124 as a 2x2
checker of 0DB/16D, two remap tables alternating per pixel row) and sets the cells to
palette 0. Music: the native title screen ($FF042, skipped by intro.s since r10) started
the menu music; menubg.s now plays $79 via $11156 when no channel in $D3D4 is on a music
sequence ($1A304's test: pointer >= ($2C708)). Measured: grey picture with 8 team pairs;
GPGX_WAV RMS > 2000 every second from the menu on. PASS menu_font, menu_team 18/240,
season_navigation 18/342, finished_season 9/108, goalie_request 41, critical 10,
critical_flash. SHA256 8cee7dfc68b2b81a3e2f330a02d923b442ef6f68f60f3a23c0acf675fb9a1041,
CRC EDCAE239.

## Main menu background = the Season Edition start-up picture (2026-10-26-r14)

jdc: the start-up picture should also be the main menu's. Menu set-up $F8168: background
asset $1F0000 via $1169A at $F8252 (plane $C000, 64 cells wide, tiles from d4 = 243, one
tile per cell: 1076 photo cells palette 3, 44 cells palette 1 behind AWAY/HOME). Palette 3
is shared with 29 menu sprites (logos, faces), so it stays; palette 2 already holds the 8
grey levels ($000, $049 .. $1B6, $1FF at indices 6, 15..10, 5) and never changes in the
menu (checked across player-card changes). New menubg.s at $2EA000, hook $F8258 (move.w
d4,$D432 / addi.w #$24,d4 -> jsr / nop): for every palette-3 cell the intro picture's tile
for that cell ($2A0000 data, levels 1..8) is written to the cell's VRAM tile through a byte
remap table, and the cell is switched to palette 2. Measured: menu snaps show the shield
behind the panels; text readable. PASS menu_font, menu_team 18/240, season_navigation
18/342, finished_season 9/108, goalie_request 41, critical 10, critical_flash. SHA256
94fb334a0c7eebb491f04db7a0575d2d9e97b7c52dafe057c53ddd6683043038, CRC F86F9366.

## Goalie outlet: a B tap during the catch is queued, not a switch (2026-10-26-r13)

jdc (Goalie Ctrl Manual): passing out from the goalie sometimes switched skaters instead
("works mostly, but sometimes"). With the puck owned by the goalie every tested tap passes
(single B at frames 151..191 after ownership: 21/21). Failure window: the puck is in or at
the goalie but $B7AA is still loose; outlet.s request only accepted owner 5/11, so a tap
that ended before ownership was an ordinary switch.s switch and no outlet followed.
Reproduced by holding the puck loose in the glove for frames 150..160 and tapping B (2
frames) at 153: r12 switched to skater 3 and the goalie kept the puck.
Fix: outlet.s request: a loose puck within 24 px (x and y) of this pad's team goalie (slot
5 / 11) counts as his, so the tap is queued in ASK and consumed (no switch); goalie_ai.s
($2FE400 pickup guard) keeps ASK when the picker is that goalie (ASK+2), other pickups still
clear it. Measured r13: no switch, ownership at 161, outlet at 163, received at 199.
Side effect: B does not switch skaters while a loose puck lies within 24 px of your goalie.
PASS goalie_request 41, critical 10, critical_flash, season_navigation 18/342,
finished_season 9/108, menu_team 18/240. SHA256
27d802ffa84516bbb4714ded125ef2e18eb81b2bbe96cf633f149241e766ed6f, CRC 576CB7E8.

## Scoring leaders: top 100 with pages, tab/sort in place (2026-10-26-r12)

jdc: the leaders page redrew everything on a tab change and showed only 18; Down should
scroll to place 100. seasonend.s moved to $2E7000 (limit $3000; the 4 KB at $2E3000 were
full); $2E3000/4/8 keep bra.w stubs (4 bytes each, a 6-byte jmp table broke +4/+8), so
$172E4, simulate.s and finished.s are unchanged. LDR = $FFE860, 100 x 8 bytes ($E800..$E85F
slots.s META/HEAD, $EA40.. leaders.s scratch for its own screen only); entries inserted by
bubbling only the new last one (sortlast). LPG $FFE538: Up/Down pages of 18 (no page past
the list), tab/sort rebuild the list in place and reset the page; rows past the list are
printed as spaces (see-through on the front plane). Rank 100 prints "100 " and drops the
space after the padded name. Footer "U/D page A tab C sort B teams Start".
Measured on jdc's finished Full: pages 1-18, 19-36 ... 91-100 (blank rows after), extra
Down no-op, A -> Regular Season page 1, C goals, Start -> menu, SRAM unchanged; season end
through the stubs reaches the playoffs. PASS finished_season 9/108, menu_team 18/240,
season_navigation 18/342, critical_flash. SHA256
6a6775ed0335d8b01097ff6fc23cff2f138fc7979674a3f8ff51a42f6e4cb36d, CRC 9D03133D.

## Season Complete menu: Scoring Leaders choice, Start returns at once (2026-10-26-r11)

jdc: after See Results, Start should go back one step at once (it went to the leaders
first, so two presses), and the menu should offer that season's scoring leaders.
finished.s: four choices (See Results / Scoring Leaders / Start New ... / Back), rows
11/14/17/20, marker cells 4 (BG). It sets seasonend.s DIRECT ($FFE536) = 1 (club page) or 2
(leaders first) around jsr $2E3008 and clears it after. seasonend.s players: DIRECT 2 jumps
to the leaders; with DIRECT set, Start on the club page returns at once (B on the leaders
still opens the clubs); the end-of-season flow clears DIRECT and keeps club page -> leaders.
verify_finished_season.py: Start returns to the menu, Down + C opens Scoring Leaders, menu
indices +1 (SEL 1 after leaders, 2 = Start New). seasonend.s has ~13 bytes left.
PASS finished_season 9/108, menu_team 18/240, season_navigation 18/342, critical_flash.
SHA256 cfa26060986cdd9d26114e85a2e1d39869064e52b50782501498cabe9721a4f3, CRC 1257FF5C.

## Season Complete / statistics: no dark boxes, dimmer background (2026-10-26-r10)

jdc: remove the dark area behind the text and darken the background a little. The text was
printed into the BACK plane ($C000, B02E = 4) where table.s's setup draws the SPORTS
pattern (palette 3, e.g. $6336): each glyph replaced a pattern cell, so its colour-0 pixels
showed the dark backdrop. Planes on these screens: B004 index 0 = $E000 front (empty,
$07FF), index 4 = $C000 back, index 8 = $B000 window (off); 128-cell rows.
Now finished.s line and seasonend.s out print to the front plane (B02E = 0); highlight
reads the front plane ($E600); the menu marker cell is in $E000. finished.s +24 setup =
$2FC004 then palette 3 colours 1..15 at 3/4 (each of B/G/R: n - n/4, even) in the fade target
buffer $BD8A..; seasonend.s calls it instead of $2FC004 (same size). Half brightness was too
dark. PASS finished_season 9/108, menu_team 18/240, season_navigation 18/342,
critical_flash. SHA256 9f6cdda867614d88064a1d77c7668ab235c1222440a14d0d4584d26ea2a54a2b,
CRC 2C153741.

## Statistics: team / tab switch in place (2026-10-26-r9)

jdc: browse between clubs as quickly as between G/A/PTS. seasonend.s club page: a team or
tab change (teamkeys) is written over the page (loadteam, build, ppage; the banner is a
direct nametable write via $1169A) when the old page was page 1 with 18 rows and the new
list also has >= 18 skaters; page 2 (goalies) or a shorter list still gets the full redraw
so no stale rows remain. seasonend.s hit its 4 KB limit (4118 bytes): hush moved to
finished.s +20 ($2E6014). Measured: Dallas -> Detroit -> Edmonton, tab A, Left, page 2 +
Right all correct, no fade; RMS 0 on the statistics. PASS finished_season 9/108,
menu_team 18/240, season_navigation 18/342, critical_flash. SHA256
281d6305f7d85ad1a88b49b8cd909b546dbae85bae3d7c57ad8e1f0b71d5ed10, CRC 5EA0AD12.

## Statistics: sort in place, no music (2026-10-26-r8)

jdc: the statistics page should not redraw when switching G/A/PTS, and the music should
stop there. seasonend.s: C on the club page now does nextsort/build/ppage only (on the
leaders page nextsort/leaders/lpage), no $2FC004 set-up and no fade; other keys as before.
Music: every fadein pushed sound ID $79 to $11156 (play sound; IDs >= $30 are music), which
restarted the menu music on each page; fadein now passes -1 (skipped), and players: calls
hush (all 8 channel pointers at $D3D4 = -1 as $1A304 does for the music channel, then
$1A264 voices off). $1A264 alone or $1A304 did not stop it. Measured with GPGX_WAV: RMS 0
for every half second on the statistics pages; music returns on the Season Complete menu.
The leaders page needs a few frames to rescan the league after C (no blackout).
verify_season_browser.py cannot be used after seasonend.s edits: its state was saved with
the CPU inside the old browser code (r8 fails at the first case; r7 already failed at the
final DONE marker). Instead: native cold-boot flows (club/leaders sort, SRAM unchanged) and
PASS finished_season 9/108, menu_team 18/240, season_navigation 18/342, critical_flash.
SHA256 9cd0daf7f296018e1720f4dd7aced24a64296b23ba99a6fe1534edac97119957, CRC F0BDF3CA.

## Freeze on "Resume Game" before the faceoff: sprite list overflow (2026-10-26-r7)

jdc: Continue Full Season 84/84 at FLA, Start Game -> pre-game menu shows "Resume Game",
clock blank, nothing responds (Codex saw the same on 2026-10-25). Not the save (checksum,
83/84 fine). Reproduced headless from his save; --no-crowd starts. The native list builder
($15EC0: a6 = $C018, d6 = 1, ... $15F06 end, $15F20 C2E8 = list words) has no limit; the
game intro (both teams skating in) builds ~90 sprites, up to $C2E8. crowd.s drew its 8 bar
sprites FIRST (r24 order), so the intro list ran over $C2E8..$C2EF: C2EA/C2EC = sprite
words (e.g. $075B/$EB95), C2EC bit0 = pause -> frozen menu. TL ($FFF634, menu RAM) could
also hold menu text, drawing afterglow flames first as well.
Fix: crowd.s +28 draw = the bars, called from trail.s's end-of-list hook ($15F06) and only
while a6 < $C298 (80 sprites, the VDP limit); trail draw (start of list, on top of the
goalie) only while LIFE ($FFF060, set by a real crit only) or with GOK ($FFF636 = $A5C3,
set with TL by a real crit, cleared when TL ends and in gameend). Note: $C466 is 0 during
period 1 (not a usable "in play" test).
Measured: his save starts the game (clock 300 -> 226 by frame 4500, C2EC clean); bars in
play; forced-crit shot flames identical to r6 including the afterglow over the goalie;
replay 23/24 distinct snaps. PASS: season_navigation 18/342, critical 10, critical_flash,
finished_season 9/108, menu_team 18/240. SHA256
589bc62b698d97ca33415beddaacd98bbba779c74fd1209fb71f7a769571445d, CRC DFCCBFF7.
Evidence: work/sprite-overflow-20261026/.

## See Old Season opens the Season Complete menu first (2026-10-26-r6)

jdc: wants to land in the Season Complete menu directly. finished.s view now reads the
header and goes straight to the menu; "See Results Again" is "See Results" and calls the
browser ($2E3008), returning to the menu. verify_finished_season.py presses C once at
frame 1500 (See Results) before its old browser checks. PASS: finished_season 9/108,
menu_team 18/240, season_navigation 18/342. SHA256
99dea2ea9827bb07e489dfeb8fa1a84bad46d50c6aadebf4120edd78c7e15c08, CRC 3F6A942D.

## Season Complete menu moves only the marker (2026-10-26-r5)

jdc: the menu after See Old Full/Light/Mini Season (See Results Again / Start New ... /
Back to Main Menu) felt slow, as if redrawn for every choice. It was: finished.s Up/Down
jumped to redraw (screen setup $2FC004, all lines, fade). Now Up/Down put back the old
marker cell and print "*" at the new one, no redraw/fade. The SPORTS background shares the
text plane ($C000, 128-cell rows: $11952 row << 7, words), and any printed glyph (space,
"@" = $7FF + font base) leaves a dark box, so keepbg reads the three marker cells' words
from VRAM before the first marker (BG = $FFE5C0) and unmark writes them back. Untouched
cells hold pattern words such as $632C. Measured: snaps after Down/Down/Down/Up show one
marker, clean background. Regressions PASS: finished_season 9/108, menu_team 18/240,
season_navigation 18/342, critical_flash. SHA256
18888df745faf4b97d265ff465f42364d3e1ab8f4bf6469ad5ec55e2b0ffcf46, CRC C3685531.
Evidence: work/finished-menu-fast-20261026/.

## Replay played a still picture; finished menu showed your club twice (2026-10-26-r4)

jdc: the automatic goal replay started but only showed a still image; on a finished
season's menu, Right on the Game row gave Edmonton vs Edmonton. (His missed playoffs were
legitimate: Mini 4-3-1, 9 pts, 19th of 32.)
Replay: the native Instant Replay ($9FD0) toggles play on a NEW C press (d1 bit5, $A25E:
bchg #1,$C2F0), not on C held (d3). The pad hook held C in d3 only, so the buffer stayed on
its first frame for the whole 540 frames. Now frame 2 of our replay sends C as a new press
(d1/d2/d3 = $20) and nothing after; the native loop plays the buffer (rewound at entry,
$A02A) to its end. Measured: snaps every 40 frames during the replay all differ (r3: 13 of
17 identical), exit Start/Start and play resume as before.
Menu: finished.s lockmenu set Home ($D04C) to the saved club but left a stale Away; if
Away equals Home it now takes the next club. r3 vs r4 with Away poked to Home: 0a/0a vs
0a/0b, logos FLA/EDM.
Regressions PASS: season_navigation 18/342, critical 10, critical_flash, finished_season
9/108, menu_team 18/240. SHA256 01e3e46332cfdf5ca27a14728e1d827e90aa72b46acb9975fd67a98d1c8f7065,
CRC 01D931FA. Evidence: work/replay-play-20261026/.

## Start Game hung after viewing season results (2026-10-26-r3)

jdc: deleted the finished Mini, started a new one (Edmonton vs Montreal) and the game hung
when starting the match. Reproduced from his pre-delete save in one session: See Old Mini
Season -> results/leaders -> Start New Mini Season (X+B) -> Game row to MTL -> Start ->
"Resume Game" pre-game menu, period 0 / 5:00, frozen; from a cold boot of the deleted
save the same match starts. Cause: replay.s kept its state in $FFF738.. (crowd block),
which the results/leaders/team-stats screens overwrite with text; a stale nonzero ARST made
the pad hook at $A41E (also used by the pre-game/pause menus) feed scripted input, so the
menu never saw the player's buttons. Fix: replay states are $A500..$A506 (anything else is
idle, the pad hook scripts only $A502..$A506), and crowd.s valid() (first frame of every
game) clears onetimer PEND $FFF736, replay $FFF738..$FFF73F and trail HN/HT/TL
$FFF630..$FFF635 (stale afterglow could have drawn flames from menu text).
Same flow now starts Montreal vs Edmonton and the clock runs. verify_finished_season 9/108,
verify_menu_team 18/240, verify_season_navigation 18/342, verify_goalie_request 41,
verify_critical_flash PASS; auto replay still runs ($A502..$A506).
Rule: never keep in-game state in $FFF100..$FFF7FF without a magic/validity check and a
reset at game start: menus use it as scratch.
SHA256 b6fb71635631a24f1c84c81a4802ff0ccfc821e891cb42ae63a5fe9a2c359239, CRC 4009ADA6.

## Longer crit flames, crits in shootouts (2026-10-26-r2)

jdc: the red crit animation was too short and did not show the flames; does crit work in
shootouts? Measured: a natural crit flash lasted 18 frames (goal / save ends it); the trail
needed the puck's sprite in the 64-sprite list. trail.s now takes the puck's position from
its x/y/height and the camera (screen x = x - $BD1C + 285, y = $BD18 - y + 233 - height,
sprite coordinates; constant over 35 samples), list search only if $C2EC bit7; six flames
(2..7 frames back: big, big, medium, medium, small, small) and a 24-frame afterglow after
the flash ends (the tail dies out: 6 -> 0 flames). RAM $FFF634 TL. Code grew past 256 bytes:
tiles moved to $2E4200.
Shootouts: the crit roll and +25% power always worked, but $C2EA bit0 (clock stopped) is
set throughout a shootout, so critical_flash ended the red puck after one frame.
critical_flash.s now skips that check when $C2FA bit0 (shootout); possession still ends it.
Shootout test (native Shootout mode, forced crit): flash 127 -> lasts about 160 frames,
red puck and flames up to the goalie.
verify_critical_flash, verify_critical 10, verify_season_navigation 18/342 PASS;
verify_shootout: 12 pass, then the known playoff fixture case (also on 2026-10-26).
Site: version and Light/Mini now in the header, goalie section updated to today's controls.
SHA256 78b36c6e3f9a925a49d92d756ad40174f7d9b494004b04544c2fe7d258ef77df, CRC 3DBBCA6E.

## Completed slots open results (2026-10-26)

See Old Full/Light/Mini Season replaces Continue for finished slots. Start
opens saved results; after leaders, view again, start a new selected-slot
season (Xbox X+B confirms, Start cancels), or return. Finished slots retain
the saved club and show Season finished. Fixes fictitious/unrecorded mirror
matches; r30 fixed only the stale Team text. finished.s1104 at2E6000, Start
hook F843E before settings save; seasonmenu1966, results3926 with +8 browser
export and both-pad Start-release wait. No SRAM format change.9 native flows /
108 checks,18/240 menu,12/228 navigation,347 browser pass; source rebuild,
guarded3547-byte diff and IPS verified. Actual user saves unchanged. New dated
2026-10-26 replaces25-r30 locally/site/Bodin, remaps carried. See
FINISHED_SEASONS.md and release.json for full implementation and hashes.


## Completed-season team label (2026-10-25-r30)

Main-menu Team text now follows native Home when the completed season has
FORCED=0; USERW is used only for locked/pregame season selection. This fixes
logos changing while the old name stayed. Active away games retain the user's
club. No new RAM/save field; seasonmenu1894 bytes. 18 native cases / 798 club
checks, 12 navigation cases / 228 checks, exact rebuild and IPS pass. Current
user SRAM stays byte-identical after ordinary menu selection. Statistics code
unchanged. New r30 replaces r29 everywhere, with remaps/saves/states carried.
Full cause, source, evidence and hashes: MENU_TEAM_LABEL.md and release.json.


## Statistics alignment and footer spacing (2026-10-25-r29)

Club skater and goalie headings move one character left to match the values:
column ends23/27/31/35. Leader column ends26/30/34/38 remain correct. Both
screens move headers to row6 and all18 skater rows to7..24, leaving row25
completely free of text above Xbox help26 and footer27. Page2's goalie block
also moves up one row. All players, numeric positions, inputs and saves stay
unchanged. Orange selection now uses row6 C600 and club PTS29/G23/A27;
leaders use +3 columns. seasonend3926 bytes,2E3000..2E3F55,170 bytes below trail.
347 native browser checks pass, including selected glyphs, both planes' empty
spacer row, boundary redraw counters and unchanged SRAM. Fresh production-safe
champion preview, exact rebuild, guarded591-byte UI/title/checksum diff and
original-base IPS reconstruction pass. Source/site/screenshot/docs updated;
r29 replacesr28 locally, Downloads, site and Bodin's same item with saves/remaps
carried. Private evidence:work/stats-spacing-20261025-234630/.
SHA256bd4eb6465d81e72b7a07dcffcee1093719a370eb049307ecf5d100007946bcc7,
CRC8170380A. release.json is authoritative for the current version.


## Xbox sorting-button help (2026-10-25-r28)

Both end-of-season statistics pages now explicitly show
"Sort: Xbox B / Mega Drive C" above the original footer. This is the same
sorting action on the existing six-button remap; the input code is unchanged.
Retains r27's orange G/A/PTS heading and no page-boundary redraw. Module3928
bytes,2E3000..2E3F57,168 bytes below trail.347 native browser checks, fresh
production-safe champion preview, exact rebuild and original-base IPS pass.
Guarded UI/title/checksum diff:1896 bytes vsr27,3215 vsr26. Details and memory
map:STATS_DISPLAY.md. Newr28 replacesr27 locally, Downloads, site and Bodin's
same item; saves/remaps carried.
SHA256990a751d77fe5b7a4b2fdb46c606ce36e9a604ba34ffe6a75e1c3346be505e81,
CRCF96D9E75. release.json is authoritative for the current version.


## Statistics heading colour and page boundaries (2026-10-25-r27)

Selected G/A/PTS is orange in the end-of-season club browser and scoring
leaders. Up at page1 / Down at page2 no longer redraw/fade the same screen.
seasonend3880 bytes at2E3000..2E3F27; cloned glyphs use disabled-window VRAM
B040..B09F, not the live front plane. No SRAM or gameplay change.347 native
browser checks, VRAM glyph/palette assertions and redraw-count regressions
pass; paired capture changes only selected glyphs. Exact rebuild, guarded3170
byte UI/title/checksum diff and IPS verified. Fresh local Dallas champion
preview tested against production code; battery progress/remaps carried.
Replacesr26 locally, Downloads, site and Bodin's same item. STATS_DISPLAY.md
has implementation, memory map and reproduction details.
SHA25668f8f60bd60e9058eb2a9d277ada8672ca5bca8fa66b05dfc23b9a2b368ce7e6,
CRCB7B97FA0. release.json is authoritative for the current version.


This is the note to read first when picking up the NHL 94 project. It says where everything is, how we work,
the rules, and where things stand. Then read `HACKING.md` (how the game works and how to change it) and
`CHANGELOG.md` (what has been changed and measured).





## Goalie catch input and all-team end statistics (2026-10-25-r26)

B held across a native goalie catch now reaches the outlet request before the
previous switch's HELD latch. Reproduced r25's skater4/1→0 then goalie5 catch,
with no pass; r26 serves the pending B in One and both Teammates pads. No B still
means hold. switch510/outlet750, no new RAM/save field. After Season Summary,
Left/Right cycles32 teams; A changes combined/league/playoffs tab, C sorts,
Up/Down pages; Start opens leaders, B there returns to the selected club.
seasonend3618 bytes; BTEAM FFE6FC/SCREEN FFE6FE, actual TEAM and all saves intact.
41 goalie cases,682 native browser checks with exact source totals/unchanged
SRAM,14/266 navigation, clean CPU Mini0–1, exact rebuild/guarded3313-byte diff/IPS
pass. Full cause, controls, sources and fixtures:GOALIE_AND_STATS.md.
SHA2564b42e4b6fa9079bb3bdb40376ff473bed4a0ef6116d9df98f5fef123d1047c8a,
CRC4ACB9735. Read release.json for the current version; older hash claims below
refer to their historical snapshots.

## Current release naming policy —2026-10-25

Current **Season - jdc -2026-10-25**, SHA256
2d4d53a50b2e153be6f5ebfe4b12b9e31d6b391d87dc75ecef57dd0cefed3f47,
CRCFEF0867F. The user now requires a fresh name/version for EVERY changed
release, replacing the preceding current file/playlist entry on Bodin, the
website (ROM+IPS) and Downloads. First daily versionYYYY-MM-DD, then-r2/-r3.
Migrate matching saves/states/remaps/configs and histories with content hashes;
do not retain an old filename just to match saves. release.json supplies the
builder default; --version overrides it for historical reproduction. Current
rename changes only the two header title date bytes147/177; gameplay/assets and
save contents are unchanged. See RELEASES.md for locations and complete process.
This policy supersedes historical filename-preservation text below.

## Automatic goal replay (2026-10-25-r25)

jdc: automatic replays after every goal, skippable with X + B (Genesis A + C, like Abandon
Season). experimental/replay.s at $2E5000:
- +0 tick (called every frame by crowd.s): score $C6DA + $CA3E rose (not in a shootout,
  $C2FA bit0) -> 90-frame countdown -> ARST = 1 and set $C2EC bit0 (pause request).
- +4 replaces bsr $7CF8 / bra $7938 at $7948 (main loop pause call). ARST = 1: the pause
  routine's prologue ($10F32, VBlank wait, $1A264, push $C2EC, $7E0E), then the native
  Instant Replay $9FD0 instead of the menu, then the pause routine's own exit $7D66 (pops
  $C2EC, restores the game screen). Otherwise the ordinary pause menu.
- +8 replaces tst.w $D046 / bne.w $A434 at $A41E (the replay's pad read: $11340/$11358
  give d0 dir, d1 new, d2 changed, d3 held). During the auto replay it returns "hold C"
  (play) for 540 reads (the buffer takes about 520 frames), then Start (options), release,
  Start (return to game). A + C held on either real pad jumps to the Start sequence.
- The replay starts rewound (native $A4F6 loop), A/B/C = rewind/stop/play, Start = options.
- RAM $FFF738 ARST, $FFF73A ARCNT, $FFF73C CNTDN, $FFF73E LASTG; crowd.s gameend clears them.
Tests: poked goal -> replay 402..1073, clock frozen during it, then resumes; A + C at
frame 520 -> back in play at 605. Natural Mini run: 4 games, every goal (periods 1-3 and
late in the third) replayed, games and season (4/8) continued normally.
verify_goalie_request 38, verify_critical 10, verify_critical_flash,
verify_season_navigation 18/342 PASS.
SHA256 cffacb9192269424bdcb136e60db64f9ab10d0bb0be5648376eebcee545c1f84, CRC 7B803E38.

## Crit flame trail always shows (2026-10-25-r24)

jdc: the crit trail only showed sometimes. Cause: the trail sprites were appended at the
END of the sprite list, which already holds about 51 native sprites + 8 meter sprites;
the native list stops at 64 ($40, RAM after $C218 is in use, no room to extend), so the
trail was often dropped. trail.s now has +0 record (end hook $15F06: puck position into
HIST) and +4 draw, called by crowd.s at the START of the next list, drawing HIST 2..5
frames ago. The flames now draw in front of skaters and always get a slot.
verify_critical_flash, verify_critical 10, verify_season_navigation 18/342 PASS.
SHA256 46db9ac09de9a90f866e4f0ecb8eef60e47e4c3f420938d5d77180be4af7d237, CRC 6BD98C96.

## Flame trail behind the crit puck (2026-10-25-r23)

jdc: cool speed streaks / flames behind the puck on a crit. experimental/trail.s at
$2E4000 (tiles at $2E4100), hook $15F06 at the end of the sprite list build
(cmpa.w #$C018,a6 / bne.w $15F12 -> jsr / bne.s $15F12; the routine ends with the same
compare). While $FFF060 (critical_flash lifetime) is set it finds the puck's sprite in
the list (tile = puck +$12; the shadow is +1), keeps 9 screen positions (RAM $FFF600..$FFF633,
menu-only RAM) and appends four sprites at 1/2/3/4 frames ago (big, big, medium, small)
behind everything: palette 1 colours A (crit red), C (orange), 6 (yellow-orange core).
Tiles $7FC..$7FE (VRAM $FF80..$FFDF) via the native DMA queue every 32 frames.
Pitfall: the first try at $FFA0..$FFFF (DMA ending exactly at $10000) wrapped onto tile 0
and covered the whole rink with dots. Tests: forced-crit capture shows a continuous red
tail; verify_critical_flash, verify_critical 10, verify_season_navigation 18/342 PASS.
SHA256 e6d7d5143bdd3352fa6a10a92d89edfcceb8d4599d9abaa80a7937e9e13de087, CRC 88B2C78F.

## B no longer switches skaters while your goalie's pass travels (2026-10-25-r22)

jdc: mashing the pass button while the goalie holds the puck gives no pass and the cursor
jumps between skaters as if switching. Probe (GPGX, gk5-clear fixture, B every 6 frames):
the first B is the outlet request and the goalie passes at once (frame 101 -> 103); every
further B while the puck travels hit switch.s ordinary_switch and flipped control 4 <-> 3
(the pass target stayed 4). Fix: onetimer.s +8 gkflight (puck loose, last passer = a
goalie of your team -> Z, and switch.s Q cleared); switch.s calls it before the ordinary
switch and consumes B (switch.s must stay <= $200 bytes before glass at $2FDB00, now 510).
After the fix control stays on the receiver until the catch (frame 103..135); later B
presses pass/switch natively. verify_season_navigation 18/342, verify_critical 10 PASS;
verify_goalie_request: the known cycle-sensitive clear-5-coop-p2-d10-hold1 (1-frame tap).
SHA256 1a1ce11286b94377e228fbfd939474369c7d8b63920391028c4e3055b92b53cd, CRC 05BAFC54.

## Playoff statistics apart, three tabs, sorting, scoring leaders (2026-10-25-r21)

jdc: see the scoring leaders too; with playoffs, one tab with ALL stats, one with only
the league, one with only the playoffs; if you go out, the playoffs are simulated to the
end in the background so all stats are there; filter goals / assists / points.
- Playoff block: SRAM $0002 "PO", $0010 + 100*slot player rows (slot = place in SEEDS,
  16 = scratch), $06B4 + 15*slot goalies, to $07A2. $0002..$0D1F is free: native code
  only writes the whole 8 KB on a format, User Records are off; the slot banks copy and
  checksum it with the rest. Cleared by playoffs.s setflag(1) (playoffs set up).
- season.s rowload/rowsave/gkput take their offset from postats.s ($2E2000 +0 ROWOFF,
  +4 GKOFF; same size as the replaced mulu/addi): POGAME set -> playoff block. So the
  season's own playoff games now go there, not into the league rows (r19 merged them).
- postats.s +12 others: after each of your season playoff games one game for every
  other pairing of that round; +16 finish: when you are out, every later round plays its
  series to the winners the game has already drawn ($CEF4 holds the whole drawn tree,
  re-drawn each round), one game or best of seven (0..3 losses). Uses season.s
  goals/dist/gksim via season-symbols.inc; checksum via DELTA (fixsum).
- seasonend.s moved to $2E3000 (hooks $172E4 / Simulate exit updated). After the summary:
  your team (banner, tab title, sort, skaters, Up/Down page 2 with goalies) and then the
  league's top 18 Scoring Leaders (rank, player, team, G A PTS SOG). Left/Right = tab
  (Season + Playoffs / Regular Season / Playoffs), C = sort (points, goals, assists),
  Start = next / main menu. ALL = league + playoff rows added on display.
Tests: Mini out in round 1: 15 playoff games (30 goalie GP), league rows/goalies
unchanged, checksum ok; tabs/sort/pages/leaders captured (Thompson 7 league + 1 playoff
= 8 GP). Full best-of-seven champion: 60 playoff games (16 yours + 28 + 12 + 4), league
GP 84 for all, checksum ok. verify_season_navigation 18/342, verify_critical 10,
verify_critical_flash PASS. The font has no < > (footer: "L/R tab").
SHA256 3115bde11904a3a00cb4201fa11cef8f7eff810573aed220d14417f46a803e87, CRC 3693B1E0.

## Co-op game row without at/vs (2026-10-25-r20)

jdc: switching Players One <-> Two - Teammates changed the Game row from "at" to "vs"
(co-op always plays at home), which looked misleading. seasonmenu.s gameinfo now
writes "84/84 FLA (co-op)" when Players = 3 (Two - Teammates); One keeps "at"/"vs".
Tested from a Full save at 84/84 (Edmonton, 83 simulated games): One "84/84 at FLA",
Two - Teammates "84/84 FLA (co-op)", back to One "at FLA".
SHA256 f073a6928cf8e9ade13c003d6ef94fcbe72c4030aa188a651f2d9a0c1d3828df, CRC 85089A9A.

## End-of-season summary and whole-run player statistics (2026-10-25-r19)

jdc: after the Cup (won) or the bracket (out), go on with a button to a screen with
all the statistics of the whole run. New experimental/seasonend.s at $2E1400 (+0 hook
$172E4 after the post-game tree/table and before the $172FA RAM reset, +4 from the
Simulate Match result screen, whose single jmp $172FA seasonend.py redirects).
classify (season view, valid finished season): Regular Season last game and the team
not in SEEDS -> missed (flag 5); the season's playoffs on (flag 2) and the game has
switched back to New Playoffs (D048 2/3) -> out (flag 3, round = $CEEC-1, the game
advances the round before) or champion (flag 4, your score > theirs in this game).
The flag is written once (playoffs.s new +28 setflag), so the summary shows once.
Screens use table.s's set-up (new table.s +4 = setup: background $2000, palettes,
scroll, VSRAM, no window/sprites); the native panel ($9BD8, simulate/teamstats) is
invisible in this post-tree context (wrong palette; $11594 clears VRAM). Season
Summary: banner, record W-L-OTL, points, GF-GA, result; Start -> "Season + Playoffs"
list: skaters by points (as teamstats build), G A PTS SOG, page 2 rest + goalies GP
W GA SA, Left/Right page, Start -> main menu. Start waits release/press (no skip).
A Cup win first shows the game's own Playoff Stats and Champions page (now reached).
Whole-run stats: season.s playoff games of the season (playoffs.s +32 poseason: in
progress, your team, flag 1/2) go through the normal save path (addreal/gkreal +
checksum) without round marking and league simulation; team table rows unchanged.
So teamstats/leaders also include playoff points after the season.
playoffs.s: played no longer reopens finished playoffs (flag >= 3; the post-season
standalone New Playoffs set 3 -> 2 before); spcheck treats >= 3 as over.
Tests (GPGX, work/season-end-test-20261025): Mini win -> Champions page, summary
"Stanley Cup champions", players, page 2 goalies (Thompson 11 GP = 8 + 4); Mini
loss -> bracket, "Out in the first round"; Mini missed via Simulate Match -> summary
"Missed the playoffs" (Vancouver 0-6-2); Full BO7 champion flag 4, goalie GP 84+16.
verify_season_navigation 18/342, verify_critical 10, verify_critical_flash PASS.
Known: after a season the menu still reads "Continue <slot>" (games no longer count).
SHA256 a877c327a8c66674172fce3a72359d24185ee37aeb99494efaf9febb561e7291, CRC C8CC833B.

## Season-to-playoffs test run; finished season takes no more games (2026-10-25-r18)

jdc: test that every season reaches the playoffs, the playoffs run to the end.
GPGX driver (work/season-end-test-20261025/po_hook.py): Simulate Match through
menu input for the league, then real playoff games (clock held at 1 s, score set,
Start outside play), plus fully natural games without any pokes. SRAM validated
after every game (checksum, GP, W/L, scorers, goalie GP/GA).
- Full 84/84, Light 31/31, Mini 8/8 simulated: all checks ok, Continue to Playoffs.
- Playoffs: Mini and Light single games won all four rounds (round 0..4, flag 3);
  Full best-of-seven advanced round by round to the final; Mini/Full loss in
  round 1 ends the playoffs (bracket shown, flag 3).
- After a Cup win NO champion screen appears (Stars of the Game, Exit Game, then
  the main menu with New Playoffs); after elimination the bracket is shown.
- After the season's playoffs the menu shows native New Playoffs; starting it
  sets the season's playoff flag $1210 from 3 back to 2 (standalone and season
  playoff state still interact; to be fixed).
- BUG fixed: a season that missed the playoffs still counted real Start Game
  games (natural Light run reached 45/31, team GP 44..59; Mini 10/8 in r17).
  season.s haveuser now returns when games >= length. r18 natural Mini: 8/8, all
  teams GP 8. The menu after such a season still shows "Continue <slot>" with a
  meaningless "Game 1/8" line (to be replaced by the end-of-season flow).
- Expected, not bugs: natural games show team GF > scorer goals (shootout winners
  add a team goal only) and team GA > goalie GA (empty-net goals).
SHA256 33baa63541d44a71f2e43d88a0dfcb8bcf752611598462794feaece74c7f13ef, CRC BE3CEBD9.

## Buffered one-timer for human receivers; meter scales Crit (2026-10-25-r17)

jdc: in co-op, when P2 presses/holds C while P1's pass is on its way, P2 stops and
the pass slides by. Cause: receiver input $B6C8 (C new press, puck loose, we are
$BEE0) queues action $23 (one-timer) at once; the windup ($C1B8, anim $07FC) stops
the skater and clears $BEE0, and the lead pass misses (probe: speed 0, puck past).
New experimental/onetimer.s at $2E1000 (+0 input hook $B6C8 -> $B6D0/$10750, +4
frame hook replacing jsr $FD78A at $15EF2): while the pass is in flight a press is
remembered in $FFF736 (object+1) and the receiver keeps skating; when that object
owns the puck the frame hook sets +$64 bit3 (native one-timer: power $1F at $C35E),
anim $07FC via $1073A and calls the native shot $C2F2 = immediate full-power
forehand one-timer (crit-eligible: $FFF0B0 set). A press is forgotten when the
pass is cut off. Probe: receiver catches at full speed (3902) on the same frame as
without C, puck leaves at 20264 with slap flag set; hold and mash identical. Only
human receivers (input path); passer-C native one-timer for AI receivers unchanged.
--no-onetimer keeps the native behaviour. NEAR_PX (0) can release early instead.
Crit: crowd +20 replaces move.w d0,d1/move.w #1000,d0 at $2FF462 (rating scaled
by the shooter side's percent before RNG(1000)), +24 replaces jmp $8E4C at $2FF4B8
(Team Roster Crit column, same scaling). Hard WSH -10%: Crit 77->70, 48->44, 79->72.
verify_critical 10, verify_critical_flash, verify_season_navigation 18/342 PASS;
verify_goalie_request still the known cycle-chaotic clear-5-coop-p2-d10-hold1.
SHA256 ff1ec02d17173e9a91496bd8d35ceae557f618ebbdb0a99a0e1fad34cd4507b7, CRC B71C5B22.

## Team Roster shows the meter effect; reset after every game (2026-10-25-r16)

jdc: with the meter up, McDavid's numbers in Team Roster had not changed; show the
effect, reset after each game. Roster values come from $FA9F8 (18 x rating nibble +
per-player adjustment byte at team block +$1A2, limit 99) and never read the object
ratings. Hook $8D70 (jsr $FA9F8, reached through the crit hook for every non-Crit
mode) -> crowd +12: for mode $0A (Overall and the 0..99 ratings; not Handed, Weight,
Energy, Fighting, Crit) and a2 = $C6CE/$CA32 it shows value + value*p/100 (trunc,
0..limit-1), only while MAG is valid. Game end: $172FA (bsr $169D0 / move #$2700,sr,
reached after every game via $172E0 and at boot, right before the B000..D03E reset)
-> crowd +16, which clears MAG; the meter, percents and stored bases start over and
Roster outside a game shows base values. Test: Hard, Washington human -10%: Overall
Chychrun 76 -> 69, Roy 55 -> 50, Kyrou 70 -> 63 (r15 showed 76/55/70).
verify_season_navigation 18/342, verify_critical 10, verify_critical_flash PASS.
SHA256 b6d72438d540cb6c81c96a4183744f0ebef5040e7a4796b28651fff718124c56, CRC AA7610A9.

## Difficulty Normal/Hard (2026-10-25-r15)

jdc: a start-menu choice Normal/Hard; Hard = the crowd meter is always at the
bottom for human players and full for the computer. Main menu row 9 (User Records,
hidden by fix 11) is now "Difficulty" with values Normal/Hard (strings at $209700,
offset table $2091FE+18). The fix 11 row map at $2FE232 sends row 9 to $FFF07A
(move.w #$2032,d1; no other ROM reference to $F07A) instead of User Records $D054,
which stays Off and unseen. Ten menu rows again: $F777A $14, $F7758/$F7762/$F7C08 4.
Power-on: $17C86 -> crowd +8 (fix 10 defaults, then clr $FFF07A = Normal). Not
saved to SRAM: after power-on it is Normal. seasonmenu.s lets row 9 through the
season lock (all other locked rows stay locked; tested on a running Light season).
crowd.s: Normal keeps one shared M; Hard scans objects 0..11 (+$62 bit3 human,
bit6 side) and remembers human sides for the game (cleared on a new game); human
side bar -1000 / -10%, CPU side +1000 / +10% (both human or both CPU: equal).
Ratings are now rescaled only when a side percent changes (the loader hook applies
it to new players). Code grew past 1 KB, tiles moved to $2E0800. RAM $FFF72A..735.
Tests: menu toggles Normal/Hard and back; Hard game: human WSH 20 -> 18, CPU COL
20 -> 22, bars empty/full; verify_season_navigation PASS 18/342;
verify_critical_flash PASS. verify_goalie_request is cycle-chaotic: the game takes
randomness from the VDP HV counter, so any change in main-loop cycles changes the
fixture's play from frame ~103. Proof: r14 with only 20 NOPs added before the crowd
hook fails clear-11-p2only "request leaked"; r15 fails clear-5-coop-p2-d10-hold1
"no requested pass" (a 1-frame B tap). Not a crowd logic fault; the fixtures need
to become timing-robust. SHA256 491fc96f9e810ac48cdf00054a5fa1faee45f1c4e4bc59407debc66cf5d7e6d8, CRC 27A68F5F.

## Team stats screen: no CRT, no frame notch (2026-10-25-r14)

jdc (Downloads/3.png, CHICAGO): a black notch in the title frame above "CH", and
CRT/footer need not be shown there. Notch: teamstats.s padded odd-length title
strings with a space AFTER the closing big-font code BD 07 01, which drew a black
tile in the frame; every odd-length city (CHICAGO, SEATTLE...) had it. The pad now
goes before BD 07 01. CRT: teamstats.s has its own SHOW_CRIT 0 (WITH_CRIT still
drives critical.s and the native attribute screens); SOG returns to column 34 and
column 37 is cleared after SOG so goalie SV% text cannot remain. Headless SEATTLE
capture (stats-freeze all-teams seed): no notch, no CRT, clean columns.
SHA256 a8b64f0f144b6e67a78f2ab16d817899f21b2b958a7cfa480264627c4e2ddc86, CRC 960E84A8.

## Crowd meter rows swapped (2026-10-25-r13)

jdc (Downloads/34.png): playing EDM, he scored and his bar dropped. The score box
lists the $CA32 team on the TOP row and the $C6CE team (team id $C331, called
"home" in crowd.s) on the BOTTOM row; r12 drew them the other way round. Ratings
were always applied to the right team; only the display was mirrored. crowd.s now
draws M (the $C6CE team) at y 206 and -M at y 191. Verified: test game C6CE = WSH
(id 30, bottom WAS row); poked WSH shots fill the bottom bar.
SHA256 6f9ff97b8b41e7dd6f6f4658237730ce29d5c89a827a4a7eb162854ef17880d1, CRC 8E50844E.

## Crowd momentum meters (2026-10-25-r12)

jdc asked whether a loud crowd raises ratings. Native: no. $B8A2 (the crowd meter
shown as Current/Average/Peak Level, Arena Record) is only read by $7A76 (peak/
average) and the stats page $9A9E; $B89C drives crowd sound selection ($7A9E,
table $18CFC) and PSG volume ($16976 -> $B044). Neither touches ratings.
New experimental/crowd.s (+crowd.py, --no-crowd reproduces r11 exactly) at
$2E0000, 27 tiles at $2E0400. One momentum M (-1000..1000, + = home) in RAM
$FFF700..$FFF7FF (menu-only RAM, empty in games; stack is at $FFFFF6):
+200/-200 per home/away shot on goal (team block +$00), +150/-150 per body check
(+$10); blocks $FFC6CE home, $FFCA32 away (+$364). Counters going down = new game
-> M=0. Each change of clock seconds $C468 subtracts M/128 (at least 1 toward 0),
so the pace follows the period length. PCT = M/100 (-10..+10). Ratings: the native
per-player loader $15AA4 (called when a player comes on) ends with jmp $1FF002
(hack joke-player nerf); hook $15D7C -> $2E0004 replays it, then stores +$66..+$75
as that object's base and applies the current percent. Each frame, objects 0..11
(goalies included) get base*(100+p)/100 (trunc, clamp 0..max(30,base)), p = PCT
for home, -PCT for +$62 bit6 (away). Scaled: $68..$6D, $6E, $70..$73, $75; $6A/$6B
are inverted timings (lower = better) so they get -p. Kept: $66 roster slot, $67
weight, $6F jersey, $74 flags. Only rewritten when p changes or after a reload.
Native temporary $69/$75 tweaks ($EA0E/$EAA0) save/restore inside one call, so no
clash. Display: hook $15ED4 (movea.w #$C018,a6 / moveq #1,d6) -> $2E0000 sets them
and puts 8 priority sprites first in the list (tick, fill, partial, 32x8 black bg
per team) at x 222, y 191 home / 206 away, left of the COL/WAS score box (which is
plane A, not sprites). Hidden when $C2EC bit7. Tiles go to VRAM $FC20 (tile $7E1;
H-scroll is full-screen, only $FC00..$FC03 used; reg 11 is only ever $8B00) through
the native VBlank DMA queue (a5: source.l, words, VRAM) every 32 frames. White fill
pal0 idx1, red pal1 idxA (the r11 crit red), black pal0 idx4. Tests: headless CPU
game, M reached -797 with away 5 checks vs 1 and home ratings 20 -> 19 at -7%;
verify_goalie_request PASS 38, verify_critical_flash PASS. verify_shootout: 12/18
then its fixture assertion "no normal goal before injection" fails because the
rating change alters play; the --no-crowd build passes 18/18 (shootout code is
untouched). SHA256 7280773be04f9424cf2910f20b9d020f1749d3add11737e05ce289c0247d4e91,
CRC 21A1898A.

## Bright red crit puck (2026-10-25-r11)

jdc: the red puck looked only slightly tinted. It used palette 0 index A ($064C,
the dithered rink-line red; GPGX RGB 200,68,96). Every palette-0 entry is visible
on the ice (15 green-substitution test ROMs during CPU play), so no rink colour was
changed. Rink palette 1 (ROM $59944) index A ($0062 dark green) is only used by a
few crowd shirt pixels: critical_flash.py patches $59958 $0062 -> $000E (pure red)
and critical_flash.s sets sprite palette 1 (andi #$9FFF, ori #$2000) for the red
phase; glint index 1 = pal1 $CCC. Test driver expects $2923. verify_critical_flash
PASS; forced-flash capture shows a pure red puck (232,0,0). Side effect: those
crowd shirts are red. Backhand question: classify already requires animation $07FC
with raw charge >= 31 (backhand $092E never qualifies); no change made.
SHA256 161efbdcee0cff042bc9903b89ad54b511c1c77348efce4abcbe00f02b80dae6, CRC FFD6745D.

## Season Edition intro, no EA logo (2026-10-25-r10)

jdc's own picture (Downloads/33.png -> tools/data/intro-season-edition.png) converted
by introimg.py to data/intro-season.bin (1120 tiles, 38118 bytes, same format as fix
12) and written by build_test.py over the plain ROM's intro at $2A0000 (asserted
equal to data/intro.bin first; the normal/plain release keeps the old picture).
EA logo screen $17A18 (called from $770C) keeps its VDP/plane set-up and
interrupt state, but $17A74 `movea.l #$FFAC0,a2` -> `bra.s $17AA0` skips drawing,
and $17AAA wait count 80 -> 0 (one 4-frame wait). NOP-ing the whole call blacks
the boot. Boot capture: frames 5-60 black, intro fades in ~80, menu ~550. Full CPU
season game afterwards (2-3) recorded. SHA256 b1288a0ff59e9fbbf983d55ec55dbb6d6d9b201e014801c8fd50bfbaf0efb071, CRC 57CD2624.

## Standalone native playoffs have their own context (2026-10-25-r9)

jdc asked for the thorough fix of the r8 limitation (a standalone New Playoffs
tournament could not coexist with a season in its playoffs). Native playoff state
= RAM D088..D175 (238 bytes: 10-byte password D088..D091, $18192 encode / $1803E
decode, plus packed playoff player stats unpacked by $1833A), inside the D076
settings block (SRAM 1EF6). Every 8KB image was full, so:
- User profiles at logical D20..E1F (CF36 and D45A, 2 x 128 bytes) are now shared:
  slots.s copybank copies 0..D1F and E20..1FFF only. Banks' D20..E1F are free.
  backup recomputes the bank checksum (new `sumbank`); restore ends with the
  active checksum. Old banks stay valid (restore ignores their profile copy).
- Standalone context lives in the Full bank's free range, logical 2D20:
  "NPO1", 238 bytes, sum, ~sum (`ctxsave`/`ctxload`); writing it re-sums the
  Full bank. Missing/invalid magic = no standalone playoff.
- suspend (season view -> native modes) calls ctxload after backing up the season:
  RAM D088.. and active SRAM 1EF6 get the standalone context (or zeros).
- New slots +20 `ctxhook` replaces `jsr $1A206` at $FE6B2 (end of the native
  settings/playoff save $FE696): outside the season view (TAG set, VIEW 0) it also
  saves the context, so it survives season switches and power-off. Boot always
  resumes the season view with the season's own context (no ctxload in init).
- r8's pocont hook at $F7AD4 is removed (original bytes restored).
slots.s grows (BORG 2AA000, < 2AB000). verify_season_navigation.py expectation
updated: season playoffs no longer show native Continue Playoffs (graph [2,3,4]).
Tests (GPGX headless, copy of jdc's save with Mini PO): standalone NP/7 CPU game
2-1 -> Continue Playoffs (CEEA/CEEC 0001/0000); Left to Mini PO 0007/0001 intact;
through Light, Full, Shootout, New Playoffs and back -> Continue Playoffs again;
reboot -> Mini PO 0007/0001 and Continue Playoffs 0001/0000 both present. All
four images' checksums valid, ctx magic in Full bank. verify_season_slots.py
PASS 20; verify_season_navigation.py PASS 18 / 342 checks. SHA256 5bb02972d6ea8d2a4e698f507bd7ddb6c9e4efba8219601110c0e517343de44f, CRC 9D205009.
Evidence: work/npo-ctx-20261025/.

## Native Continue Playoffs hidden for season playoffs (superseded by r9) (2026-10-25-r8)

jdc: after a Mini series the circle showed both "Continue Mini PO" and native
"Continue Playoffs" — the same season playoff (native password shared by the
active slot) offered outside the season. New `season/pocont.s` (2ABC00): hook at
$F7AD4 (jsr $17CA0 -> jsr $2ABC00) runs the native decode, then, with SLOTVIEW
$FFF06C == 0 and the active slot's SRAM $1210 playoff flag 1 or 2, clears
CEEA/CEEC so native validation skips Continue (as for no saved playoff).
Season view unchanged. Tests (GPGX headless): jdc's save (Mini PO active) r7
circle Mini PO -> Continue Playoffs -> New Playoffs..., r8 Mini PO -> New Playoffs
-> NP/7 -> Shootout -> Full -> Light -> Mini PO. Fresh save, standalone New
Playoffs/7 game, one CPU game: r8 shows Continue Playoffs. With Mini PO active, a
standalone New Playoffs game left Mini PO intact (CEEA/CEEC 0007/0001 before and
after, same fixture). SHA256 b0b82332af37f3737c41cd6fc3d1b2e512c17501f1f5c29f4c0e79e5768a57e5, CRC 1252F70E.

## Abandon dialog shows Xbox buttons (2026-10-25-r7)

jdc (Xbox pad) read "A + C" literally. newseason.s confirm line is now
"A + C (Xbox X + B) = yes, abandon" (fits at col 4). Headless check: dialog renders,
A+C still clears the slot (Light 0 GP, checksum OK). SHA256 c448ab56ec959b5bd5988b5b0677249491dc310cd0ebcea5d3ddc70e0627a4a8, CRC 2E5FC2C0.

## Abandon Season in the in-game pause menus (2026-10-25-r6)

jdc wanted to abandon a season from inside a game. build_season.py copies the
pause lists $19700 (normal, ref $7D1C), $1988C (after timeout, refs $7D44/$9D84)
and $19C04 (intermission table refs $131D0/D4/D8/E4/E8/EC) to $2AB100.. with an
appended "Abandon Season" entry -> $2FA800, and repoints the refs (asserted).
Shootout list $19664 and post-game Exit lists unchanged. newseason.s now starts
with a guard: proceed if $D048==0, or 1..3 with SLOTVIEW $FFF06C != 0; otherwise
jmp $9CD8 (shared lists also serve standalone playoffs). Headless on a copy of
jdc's save (Light 29 GP): pause -> Down x16 -> Abandon -> Start returns to the pause
menu, Resume continues (clock 141 -> 133 s); A+C -> main menu New Season Light 1/31.
Light slot 0 GP, Full/Mini statistics identical, checksums OK. Remap carried.
Controller note: Xbox pad A/B/X/Y = Genesis B/C/A/Y with device 513.
SHA256 84d77895c38452f5e96c8ed3ae43397b4a2445a0abe44c70353ac2b5e479c8bf, CRC 1B9CF260.

## Restart Season renamed Abandon Season (2026-10-25-r5)

jdc: "Exit Season" was considered but rejected as misleading (the entry erases the
slot); chose **Abandon Season**. Same 18-char pre-game item in build_season.py
(league and playoff lists), dialog title, "Abandon this season?" and
"A + C  = yes, abandon it" in newseason.s. Behaviour unchanged: A+C clears only the
selected slot and returns to the main menu on New Season <format>, where Team (and
the first opponent) can be chosen; Start cancels. Headless check on a copy of jdc's
save: Light -> Abandon -> New Season Light 1/31, Team Edmonton -> Florida.
Controller remap renamed with the ROM (local + Bodin). SHA256 8960475c5570a3fc6dbed5ccb2bae272c83243cbdcbb0134bb6728be8cc2561f, CRC E3F44C8D.

## Simulated games scale with Per. Length (2026-10-25-r4)

jdc asked that simulated fixtures match the period length he plays. Measured 118
complete headless CPU-vs-CPU games (GPGX, r3, both pads CPU via $C320/$C322, Start
at intermissions; hook/runner work/simlength-20261025/cpu_hook.py, one.sh), 30 per
length (2 seven-minute runs timed out). Regulation goals/team (sd) and SOG/team:
5 min 1.32 (1.15) / 11.7; 7 min 1.93 (1.27) / 16.8; 10 min 2.82 (1.78) / 24.8;
20 min 5.70 (2.31) / 49.5. Scoring is ~0.28 goals per team-minute.
`season/season.s`: `goals` tries = 4/6/8/16 by $FFD050 (0=5,1=7,2=10,3=20; other
values -> index 2 = previous 8-try model); chance formula 60+strength/4 unchanged.
`dist` shots = base 5/9/15/39 + rnd(14/16/20/22) (was 22+rnd 16). New `lenix`,
tables `tries`/`shotb`. season.s 3938 -> 3994 bytes (limit $2F9000). Applies to
league simulation and Simulate Match (shared GOALS_FN/DIST_FN). Per. Length is
locked after game 1, so a season uses one model. Played games unchanged.
Check: fresh Mini, 8 Simulate rounds per length (256 team games each): GF/team
1.50/2.14/2.80/5.55 (incl. OT goal), shots 11.5/16.6/24.1/48.6; checksums OK and
readsram problems 0. Played CPU games at 5 and 20 min completed and recorded.
Saves not migrated (jdc). SHA256 0d16d6e10d3e0b677f60af5d9d3878a748ab92d885530af0c8fb73e703c3a7bc, CRC EF17AFA2.

## Team-stats SOG column cleared (2026-10-25-r3)

jdc saw LAK shots like `141%`, `811`, `1289` (stored 143/82/131 — save data correct).
In WITH_CRIT, skater rows write SOG at cols 33-35 and CRT at 37-39; col 36 was never
written, so after page 2 (goalie header `SV%` and SV% values reach col 36) returning to
page 1 left those characters on display rows 10-12. `season/teamstats.s` now writes a
space at col 36 after SOG. Block $2FB000 grows by 4 bytes (diff $2FB25D..$2FB6DF, end
$2FB6DF). Headless GPGX on a copy of jdc's save: standings -> COL -> page 2 -> page 1:
r2 shows `49%`/`240`, r3 clean. Saves not migrated for r3 at jdc's request ("not
important"). SHA256 1a2d72de66d8919e997fbe3263539dcc114a247777f98c1b56e1bec93f2ed6d0, CRC AC761309.

## Opponent choice only before game 1 (2026-10-25-r2)

jdc clarified the 2026-10-23 request: Left/Right on the main-menu Game row was meant
to vary the FIRST opponent of a new season only; after that the schedule is fixed.
`season/opponents.s` `change` (+4 entry, $2FD600) now returns without touching SRAM
unless the season header has a pending new format (+12 != 0) or zero games played
(+8 == 0). Get/capture/commit, the round mask, schedule, simulation and SRAM layout
are unchanged; block grows 630 -> 646 bytes (ends $2FD885, below switch.s $2FD900).
Only $2FD600..$2FD885 plus header/checksum differ from 2026-10-25.
Tests (Genesis Plus GX headless, private save copies): jdc's Light 30/31 Right/Left —
old ROM EDM->FLA, r2 stays EDM; new Mini pending and empty Full GP=0 — both ROMs
COL->SEA->NJD; Mini after one simulated game 2/8 — old NJD->BUF, r2 stays NJD.
Exact baseline rebuild of 2026-10-25 matched before the change. Full CPU game not
rerun (menu-only change). Note: verify_season_teams.py's mid-season opponent changes
now expect the lock. SHA256 c6c08289c13ef5b82ba66b87e69182e0f417a1321ba4f2fec33db00ff9fe60af, CRC 98017587.
Evidence: ~/.config/retroarch/roms/claude/work/lock-opponent-20261025/.

## Current: critical slapshots and Twin Cobra sound (2026-10-25)

Only full forehand windups (native animation07FC, unscaled charge>=31) now roll
Crit Chance; quick wrist releases and backhands do not. Forehand one-timers
qualify. Critical shots play the selected Twin Cobra Digital effect13 (local
clip19) once, alongside the existing red puck. CRT chance/25% bonus, ratings and
saves remain unchanged for qualifying shots. New critical_slap.s126 bytes at
2AD000, extended sound/PCM tables2AD200/2AD400, stream2AD480 and4497-byte PCM
asset2AE000..2AF190; transientFFF0B0 is reserved. Existing sound IDs, samples,
Z80 driver and music are retained. See CRITICAL_SLAP.md for exact hooks, original
sample extraction, source links, native playback and all validation.
110,000 native boundary/probability/sound/physics trials +110,000 without flash,
eight live audio/pad cases, music continuity and a clean full CPU Mini game pass.
Exact rebuild/legacy baseline and guarded5297-byte diff/IPS reconstruction pass.
Current dated SHA2560312bd6009741447726088115801a9e2cd0bd9f385b616743e393cadd3396a1a,
CRCFC0D6B4F. --legacy-crit-shots reproduces the preceding portrait build; older
entries/hashes below describe historical feature snapshots. Dated filename and
three test seasons carry over; no additional backup requested by jdc.

## Correct player-card portraits (2026-10-25)

The dated build now maps all832 cards to the reviewed player identity, instead
of retaining the pre-roster-update face.702 slot associations reuse existing
portraits,123 use new real NHL photos and7 have an explicit neutral silhouette.
586 pointers repaired;130 new874-byte records at2B0000..2CBBD3. Shared palette,
all gameplay code, roster/stat/save data and SRAM layout are unchanged.
Source: experimental/player_cards.py, prepare_player_cards.py, player_cards/
manifest.json and native verify_player_cards.py. Builds are offline; optional
--no-player-cards reproduces the preceding OT/shootout ROM exactly. Unknown
identities fail rather than assigning an unrelated face. Detailed IDs, sources,
seven missing photos, aliases, ROM format and validation: PLAYER_CARDS.md.
832 native lookups/decodes,28 paired WSH and28 EDM screenshots, byte-identical
rebuild and clean CPU1–4 pass. Current SHA256
0e5c8ee01f69d7219456d414a15b58fe593c0b957c5630886d138027822d0461,
CRC694C73C2. Dated filename/save matching and earlier releases are retained.

## Ten-minute OT, then shootouts (2026-10-25)

The dated build now uses10-minute sudden-death OT in played league and playoff
games, followed by three shootout attempts per club, early clinch and paired
sudden death. In Teammates, P1/P2 alternate their club's attempts on either side.
The native shootout panel displays attempts/score; the winner adds one final
team goal, without adding player/keeper goals or shots. League OTW/OTL and
native BO7/single-game playoff progression work normally. No SRAM layout change.
Full implementation,900-byte2AC000 code, FFF090..0AF reservation, nine native
hooks, Three Stars/late-penalty guards and18 integration cases are documented in
EXPERIMENTAL_BUILD.md. Source: experimental/shootout.s, verify_shootout.py and
testcases/shootout_hook.py. Save reader accepts shootout deciders/empty-net GA.
Overtime snapshot SHA25627349dff9ef46db0b1115f607535c72e03573a91896af03fa25df1b16f6443f7,
CRC57008214. Earlier releases are historical; dated filename/save matching stay.

## Current experimental profile (2026-10-25)

Fixed corrupt/blurry original main-menu values while cycling native cup modes.
Unchanged mode writes now bypass relabel before SLOTKEY handling, preserving the
native small-font flag through the value redraw. Menu remains1836 bytes.16 full
circle cases/3608 native text calls have zero wrong-font selections, with direct
before/after screenshots;20 slot cases,615 club checks and CPU1–2 OT also pass.
Full cause/test/current hash:the newest section in EXPERIMENTAL_BUILD.md.

Fixed a startup menu lock when Right from an empty Mini reached native
Continue Playoffs without a password. SLOTKEY now blocks repeated season-slot
transitions while allowing standalone native validation and season re-entry.
Menu1836 bytes.18 navigation cases/348 circle/reverse checkpoints, the exact
user save,20 slot cases,615 club checks and a full CPU4–1 pass. Read the new
menu-freeze section in EXPERIMENTAL_BUILD.md before changing the mode writer.

Full, Light and Mini now have three independent battery-save slots. Left/Right
on Play Mode resumes the selected format's own team, games, statistics and
playoffs; Restart Season resets only that slot (also in season-playoff menus).
New Full84 / legacy82 / Light31 / Mini8 and their playoff lengths are retained.
Existing running saves migrate in-ROM without an external save-file rewrite.
SRAM grows to32KB logical/64KB physical when the snapshots are populated. Keep
the full save file; readsram.py supports --slot full/light/mini. Current source:
slots.s at2AA000, menu1836, playoffs1074, newseason340. RAMFFF068..06F and
FFE800..E85F are reserved. See the new three-slot section in the build guide.

Critical shots now play the native glass-break sound once at release, alongside
the red flash. Normal shots retain their sound; chance/power/physics and saves
are unchanged. critical_sound.s/.py uses12 bytes at2FF600 and the critical
shot's existing stack argument, with no new RAM. --no-crit-sound disables it;
--no-crit-flash can keep sound alone. The dated2026-10-24 filename stays to
preserve save/remap matching. Full details and tests are in the build guide.

Fixed identical/stale team logos when choosing your season club: all three
formats now recalculate both clubs before native logo loading. The actual
schedule was already distinct; it now matches the visible logos. One/Teammates
changes during a continued away game also refresh both sides correctly.
That logo-fix snapshot used1894 bytes; current slot menu is1836. See the new menu-fix section in the build guide.

Critical shots now make only the puck blink red/ordinary (four visible renders
each). The effect ends on possession, dead puck, another shot or bounded expiry;
shot power/chance and ratings are unchanged. New source critical_flash.s/.py
uses ROM2FF500/2FFE00 and RAMFFF060..FFF063. Read the new visual-effect section
in EXPERIMENTAL_BUILD before changing sprite DMA or reusing those RAM bytes.

The dated build now adds a separate Crit Chance rating (0..100) per skater:
100 gives10% chance per released shot of +25% power; 50 gives5%. Better
offensive ratings get higher values. Team Roster has a Crit Chance attribute;
season player statistics and this site's team pages show CRT/Crit. Native
ratings and battery saves are preserved. See the critical-shot section in the
experimental guide for source, table locations, overrides and regression tests.

The shared warning sound at penalty expiry is now silent: the period warning
remains three pulses at40, without a duplicate when a penalty ends at60 or any
other match-clock time. Penalty release/countdown ticks remain native.
The dated build now holds the AI goalie's puck until a fresh B press. In One,
P1 requests it; in Teammates, P1/P2 requests a pass to their own controlled skater.
A tap waits through recovery; first request wins. Native prolonged-hold faceoff,
manual/Y and CPU-only AI remain. The previous automatic clear/pressure outlet
policy is superseded. New request RAMFFF064..067; see the build guide.
The dated build adds 50% breaking odds for hard, high puck impacts on flat
far-end glass, with shards positioned at the hit pane. Native low/soft/other
board behavior remains. Full source and precise bounds are in the guide below.
The dated build now calls the full mode **New Full Season**, with 84 games for
new seasons; Continue preserves legacy 82-game saves. Light/Mini remain 31/8.
The dated 2026-10-24 test gives diving goalie catches the ordinary possession
window (180 ticks, previously 5) and adds press-edge B switching to another skater, and includes 8-game Mini, opponent selection on the Game row, instant
simulation, goalie/Y changes, 0:40 warning, shorter penalties and statistics fixes.
Read [EXPERIMENTAL_BUILD.md](https://dela.jdc.se/nhl94/notes.html#EXPERIMENTAL_BUILD.md) for all current source paths,
addresses, save layout, exact build command/hashes and evidence. All source is on
the website in `tools/season/` and `tools/experimental/`. The complete test builder
writes an explicit private output; `tools/build.sh` still installs/publishes the
normal release profile, so do not use it for an isolated test update. The earlier public
ROM/IPS remain unchanged; separate dated test ROM/IPS downloads are available.

## Where everything is

| What | Where |
|---|---|
| Project / working folder | `~/mount/truenas/www/dela.jdc.se/nhl94/` (public site https://dela.jdc.se/nhl94/) |
| Tools and notes | `nhl94/tools/` and `nhl94/tools/notes/` |
| Current jdc ROM | `.workspace/NHL 2026 - 94 Edition v5.1W - Season - jdc - 2026-10-29.bin` (basename/version from `tools/experimental/release.json`) |
| Base ROM (unpatched v5.1W) | `.workspace/NHL 2026 - 94 Edition v5.1W.bin.orig.bak`, md5 8f863ecb108c1d213953bada9b98f4f2 |
| Private work files | `.workspace/work/` — all saved states, screenshots, backups, experiments and continuation records |
| Website pages | `index.html` (patch notes, Teams grid, Tools), `team.html?t=XXX` (one team, reads `teams.json`), `notes.html` (these notes), `tools/index.php` (file list) |
| Public downloads | `download/NHL 2026 - 94 Edition v5.1W - Seasons.bin`, `download/NHL 2026 - 94 Edition v5.1W.bin`, and `download/NHL 2026 - 94 Edition v5.1W - Seasons.ips` |

## Rules

- **Never open a window when testing.** jdc plays on one of the screens. Run MAME only through `tools/run.sh`
  (no DISPLAY/Wayland, SDL dummy driver, `nice -n 19`). `-video none` alone is not enough.
- Keep public release files together in `download/`. `tools/build.sh` refreshes both ROMs and the IPS patch after a build.
- Always build from the base ROM with `tools/build.sh`; it backs up the previous ROM and installs the new one.
- Measure before installing: compare against the original with the same test, and run a full CPU game as a stability check.
- At the end of a session: add what was done, in plain words, at the top of `SESSION_LOG.md` (the summary given to jdc).
- After every change: update `CHANGELOG.md` (what and measured numbers) and `HACKING.md` (addresses, free space), run
  `python3 tools/make_site_data.py` if rosters changed, and update `index.html` if the change is visible to players.
- Notes and the website are in English. Talk to jdc in Swedish.
- Don't copy EA's graphics or sound. Screenshots of the game to explain a change are fine.

## Standard tasks

    cd ~/mount/truenas/www/dela.jdc.se/nhl94/tools
    ./build.sh                    # build + install the ROM, back up the old one, rewrite the IPS
    ./update_rosters.sh           # refresh all rosters from the NHL + DailyFaceoff, then build
    python3 make_site_data.py     # refresh teams.json for the team pages
    ./run.sh cmds.txt             # headless MAME run (see HACKING.md, Testing)

Starting a ROM for jdc to play (he asked for this, so he always tests the right file; Claude's own tests stay headless):

    cd ~/.local/share/Steam/steamapps/common/RetroArch && setsid ./retroarch.sh -L cores/genesis_plus_gx_libretro.so "<rom>" >/dev/null 2>&1 < /dev/null &

RetroArch (Steam) keeps battery saves in `~/.local/share/Steam/steamapps/common/RetroArch/saves/Genesis Plus GX/<rom name>.srm`
(16 KB, data on the odd bytes; MAME's `.nv` uses the even bytes).

A new game change: add it to `patch.py` with a flag to switch it off (pattern in HACKING.md), test with `run.sh`,
build, update the notes and the site.

A hand-made team: edit `rosters/<TEAM>.json` and set `"manual": true` so `update_rosters.sh` leaves it alone.

A single player's ratings (e.g. "he should be a star"): add him to `rosters/overrides.json` (name → 14 hex nibbles),
then `python3 gen.py rosters EDM` (EDM = the manual teams), `./build.sh`, `python3 make_site_data.py`.

## Where things stand (2026-10-21)

Installed ROM md5: see `teams.json` (`rom_md5`) or `md5sum` the ROM. Changes in it:

1. Co-op passing prefers the other human; wider receive radius for him (40 since 2026-10-22).
2. Goalie passes are never blind and a bit softer.
3. Blue-line shots: from distance 160 (about 20 px inside the blue line) the goalie misses 15 % of shots. About 2.8 % of
   such shots score (original 0.3 %). Shootouts are unaffected.
4. The off-screen arrow (goalie in shootouts) is placed correctly (was 32 px too far left).
5. Fighting is just Off / On and the setting holds for the whole game (it used to be overwritten mid-game).
6. All 32 rosters and lines as of 2026-10-21.
7. Hand-set star ratings for Frondell and Stenberg; the hack's placeholder ratings (all 1s) replaced by model ratings.
8. No player in two lines; Fighting shows N/A when Penalties is Off.
9. **Season mode** (season ROM `~/.config/retroarch/roms/claude/MEGAdriveHARDJOB/NHL94-season.bin`, the one on the site):
   Regular Season = an 82-game season (Continue / New Season), standings by division, scoring leaders, every player's
   goals/assists/shots, simulated league. Details in `SEASON_MODE.md`.
10. Y on a 6-button pad takes your goalie (Goalie Ctrl Manual, no puck); `ygoalie.s`, assembled by `build.sh`.

### How the rosters were made

- Each team's roster comes from the NHL's official API and its lines from DailyFaceoff.
- Every trio is turned so each player plays his official NHL position.
- Each team keeps its 26 slots and its number of goalies.
- Order when picking players: dressed tonight → rest of the roster → injured → prospects. Utah and Vancouver had too few
  players, so they were filled with old ROM players who are on no NHL team.
- Players who were already in the ROM keep the hack's ratings, even if they changed teams. New players are rated from
  NHL stats with the kNN model; players with few or no NHL games get typical depth-player ratings.
- Edmonton was done by hand and approved by jdc; it is marked `"manual": true`.
- Tested: all 32 rosters read back exactly from the ROM; full CPU games with eight teams (BOS–CAR, CBJ–OTT, NSH–TOR,
  SEA–ANA) and WPG–EDM ran without errors. The Line Editor was only checked for Edmonton.

### Known gaps

- Montreal has 3 goalies in the hack although its team flag says 2; kept as it was.
- Goalie ratings are only partly mapped (weight, agility, speed, endurance known; the rest shown as numbered columns).
- Nibble 11 of skater ratings is unknown (values 1–5).
- The hack has weak-looking but real ratings for some veterans (e.g. Kulikov, Boqvist); only exact all-1 placeholders are replaced.
- The first of the 8 line entries in a team record has an unknown purpose (set like Scoring 1 with another extra skater).
- Nothing has been play-tested with real pads by Claude; jdc plays and reports.

### Ideas not done yet

- Playoffs after game 82, goalie stats in the season (see SEASON_MODE.md).

- Let player 2's skater steer toward an incoming pass like CPU players do.
- Higher fight frequency (lower threshold) if jdc wants more fights.
- A view on the site that compares two teams.

## Tools zip (2026-10-26)

`tools/make_tools_zip.py` packs all of tools/ (without __pycache__) plus a README with the current
version/SHA into `download/nhl94-tools.zip`, linked on the site. The release deploy_local.py
template (work/replay-play-20261026/) runs it after every release; run it by hand if releasing
another way.
