# Accepted keeper outlet requests — 2026-10-30-r4

jdc confirmed that the reported button is physical **A (pass)**, corresponding
to logical Genesis B in his installed controller mapping. A pass request should
select an eligible nearby skater and ask the AI keeper to pass to that pad.

The reproduced defect is a narrow timeout race. The input request is accepted
while the native keeper possession counter is still nonnegative, but selection
and the native keeper decision can occur on different ticks. The old counter
can expire in between, leaving only the cursor change visible.

## Correction and boundaries

Only the first valid publication of a previously empty pending request calls
`experimental/outlet_deadline.s`, a 98-byte helper at **2DB400**. It is called
from `outlet.s` and the adapter's raw input publisher in `fourgoalie.s`, inside
the existing interrupt-masked publication. With an AI keeper still holding the
puck, a matching request and live ordinary play, a remaining counter of 0–3 is
raised to four native processing ticks. Every other object field, register,
stack and CCR is retained. The native pass and receiver/vector hooks are used.
A held button does not repeatedly extend the timer; no new RAM is allocated.

A negative counter means the native timeout is already committed: captured
states contain queued event **0805** before the visible STOP flag appears.
Those requests retain the native whistle. Actual stopped play, manual keepers,
shootouts, penalty shots, invalid ownership and mismatched requests do not get
this reservation. There is no global frame, animation or physics hook, and no
native event queue is edited. The ordinary possession duration remains native
unless a valid request reaches this final processing window.

## Exact-build tests

- **1,920 paired input cases per ROM**, using two native keeper captures and
  every tested onset with 1/2/5/12-frame taps. Timer and play flags before each
  press agree between the two ROMs. Of 1,792 requests inside the native deadline,
  r3 passes 1,776; r4 passes all 1,792. All 128 late/stopped requests still give
  no pass. These are replay branches, not independent matches.
- **38 native ABI/guard cases** check both keeper sides, whole objects, all
  registers, stack and CCR; **108 paired native input contexts** cover B/Y/C,
  selection, deadline and stopped controls. The original 41 ordinary keeper
  cases, adapter controls and all other shared controls checks also pass.
- **All 70 shared checks executed: 69 pass.** The inherited `finisher_hold`
  failure is unchanged: native receiver distance 1.9046278288761302 versus held
  2.777268439788352; early one-timers 2 versus 2, with four Normal/Easy controls
  unchanged. The strict release gate remains **FAILED**. This maintenance
  retains the previously selected playtest's documented AI limitation.
- The first full run rejected the old keeper-only ROM guard for the new input
  module. Its replacement separately guards r2→r3 keeper changes and r3→r4
  input changes, and demands exact current no-B trajectories against **40 fresh
  r3 controls (24,000 frames)**. The 40 fresh r2 controls still reproduce the
  immutable original traces. The affected check was rerun; all other 69 checks
  retain the same ROM and unchanged test implementations. The initial failed
  report and logs remain preserved; report source-refresh provenance is explicit.
- Five rebuild/rollback comparisons pass. Initially shared build-tree attempts
  conflicted; failed comparisons were repeated in isolated trees. The final
  guarded comparison changes **543 bytes** against the version-only parent,
  confined to the two input modules, new helper and checksum. Original-base
  IPS reconstruction is exact; original MD5 `8f863ecb108c1d213953bada9b98f4f2`.

Source: `outlet_deadline.s`, `outlet.s`, `fourgoalie.s`, `build_test.py`,
`verify_outlet_deadline.py` and `outlet_deadline_gameplay_hook.py`.
The shared runner adds `--outlet-reference exact-r3.bin`. `--jobs 3 --keep-going`
may run independent suites concurrently; every required check still executes.
Native capture fixtures are private under the canonical work directory;
`verify_outlet_deadline.py` checks their exact hashes. No user SRAM/state is in
public source downloads.

## Rebuild and rollback

```sh
python3 tools/experimental/build_test.py base.bin r4.bin --version 2026-10-30-r4
python3 tools/experimental/build_test.py base.bin r3.bin \
  --legacy-outlet-deadline --version 2026-10-30-r3
```

The second command recreates exact preceding r3 SHA
`3453b3fba1a4e3b76c8f859311a499f3f2bf8d9a493280f43ad41eca8b784057`.
Add `--legacy-goalie-lateral --version 2026-10-30-r2` instead of the final
version flag to recreate exact r2. Historical rollback commands in older notes
also need `--legacy-outlet-deadline` with the current builder.

## Recording and remaining reports

The pre-test SRAM clone and post-test file are byte-identical (65,536 bytes).
Core savestates omit SRAM, so future recordings preserve it separately along
with the ROM/core/config/remap. Installed [RetroArch 1.22.2](https://github.com/libretro/RetroArch/blob/cf30bc29bb/input/bsv/bsvmovie.c#L732) has a Zstd checkpoint
length bug; the original private input recording is retained, and only a
private derivative's size metadata was corrected. Recorded input bytes did not
change. Both native frontend playback and the private input driver show the same
keeper/puck/control/score/clock and whistle state at the two inspected A presses;
whole RAM is **not** claimed identical. Those particular presses were already
past the whistle and are not the valid-deadline regression fixed here.

This release repairs the independently reproduced request race. Human retesting
can still reveal another cause of the report. The intermittent whole-formation
faceoff alignment report remains open. Hard strength search remains stopped.

ROM SHA256 `4dd53440a7a4b4b7920a432b1e70a18e0ddb43e2a57a553b65cd052a0a0dca50`;
CRC32 `4884D486`. Fresh progress applies on release; remaps/config/history migrate.

## Delivery

The exact r4 ROM/IPS and current source are published. Downloads and the canonical
ROM folder contain r4; preceding current downloads were replaced. Local old/new
progress cleanup completed before launch. Controller remap SHA256
`0157a8c88d6785a71907641e892f1cffbd9b1086e1cf000c9f3102d9d302af74`
and per-game play-history/config hashes were preserved through migration.
The r4 game was started for the user with native input recording. Initial SRAM
absence/cold defaults, ROM/core/config/remap and replay metadata were retained
privately. This recording session uses uncompressed checkpoints to avoid the
installed replay writer defect; session overrides do not overwrite the installed
frontend configuration. Native game progress saves remain enabled.

Bodin's existing playlist update is queued for r4; the previous r3 delivery
worker was superseded. Remote installation is not confirmed while the host is
unavailable. No active RetroArch was closed by this work.
