Connection audit: every connect/disconnect scenario handled + verified

Systematic actor-death x lifecycle-phase sweep (operator request after
the WaitingForEgg zombie).  New handling, all live-verified:

1. PRE-EGG console-pad loss (the real zombie phase): before the egg the
   pod's only link is the console pad -- the game socket doesn't exist,
   so the earlier RelayGameDown hook could never fire (the verification
   run itself caught this).  Detection now lives in
   HostDisconnectedMessageHandler ConsoleHostType (relayMode +
   pre-scene -> BTRelayRejoinNow); mesh keeps 1995 behavior.
2. MID-MISSION relay loss: the STOP is relay-sent (1995: the console
   owned the clock), so a pod losing its relay mid-match played a
   peer-less FOREVER-mission.  RelayGameDown (scene presented) now
   posts StopMissionMessage at +15s -> normal end -> lobby relaunch.
3. POST-RELEASE pod death stalled the round (survivors wait forever on
   the dead peer's connection gate): relay _abort_round -> route -11
   REJOIN to survivors -> everyone re-seats in seconds (reset-first
   ordering makes the drop cascade re-trigger-proof).
4. BTRelayRejoinNow carries BT_SEAT_CLAIM (mirrored tag): without it
   the rejoiner's own reclaim-hold blocked assignment -> ROSTER FULL
   loop for the 90s window (found by verification round 2).
5. Beacon NAT keepalive (SIO_KEEPALIVE_VALS 60s/10s) so long
   between-rounds waits can't be silently unseated.
6. BT_LOG_APPEND=1 preserves the log across lobby-loop generations
   (truncation erased round-1 verification evidence).

Verified non-issues: relay pods never bind the -net listener (no
double-launch collision on the internet path); mesh bind-fail exits
cleanly.  Full matrix + evidence: context/multiplayer.md;
scratchpad/audit_verify.sh is the repeatable rig.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-23 09:23:23 -05:00
co-authored by Claude Opus 4.8
parent 56ca2f5c13
commit c8cba8c764
4 changed files with 194 additions and 2 deletions
+32
View File
@@ -468,6 +468,38 @@ phase). Plan: `~/.claude/plans/partitioned-snuggling-piglet.md`.
per-event cost) is an OPEN perf item -- deliberately not attempted pre-8-player-night.
Diags: `[loadphase]` markers always on; the full queue dump gates on `BT_LOAD_DIAG=1`;
`GeneralEventQueue::DumpBlockers` + the named fallback in `Event::DumpData`.
- **CONNECT/DISCONNECT AUDIT (2026-07-23) [T2, every cell verified]**: systematic sweep of
actor-death x lifecycle-phase, requested by the operator after the WaitingForEgg zombie.
VERIFIED-HANDLED (previously): relay-not-up patient retry; seated-quit seat-free+reclaim;
pre-launch-quit trim; mid-mission peer death (frozen mech, authentic; matchcheck flags);
normal end lobby loop; upload-fail file-keep; scanner/HELLO validation; mass-drop crash fix.
FIXED THIS AUDIT (all live-verified via scratchpad/audit_verify.sh + focused runs):
1. **Pre-egg console-pad loss** (the REAL zombie phase): before the egg, the pod's ONLY link
is the console pad -- the game socket doesn't exist yet, so the first fix's RelayGameDown
hook could never fire (caught by the verification itself). Detection now lives in
`HostDisconnectedMessageHandler` case ConsoleHostType (relayMode && !scene-presented ->
`BTRelayRejoinNow`); mesh keeps 1995 console-less behavior. Verified 2x in one run.
2. **Post-egg pre-mission relay loss**: RelayGameDown -> rejoin (the original fix). ✓
3. **Mid-mission relay loss = forever-mission**: the STOP is relay-sent (1995: console owns
the clock), so a pod losing its relay mid-match played alone forever. Now RelayGameDown
(scene presented) posts `StopMissionMessage` at +15s -> normal end path -> lobby relaunch
-> patient wait. Verified: scene LIVE -> relay killed -> 15s -> clean RunMissions exit.
4. **Post-release pod death stalls the round**: survivors wait forever on the dead peer's
connection gate. Relay `_abort_round` (registered-conn drop while eggs_released &&
launches<2): reset-first (re-trigger-proof), then route **-11 REJOIN** to survivors ->
`BTRelayRejoinNow` -> everyone re-seats in seconds. Verified: kill-during-load ->
ROUND ABORT -> survivor rejoined -> seat RECLAIMED immediately.
5. **`BTRelayRejoinNow` carries `BT_SEAT_CLAIM`** (from the mirrored tag): without it the
rejoiner's OWN reclaim-hold blocked generic assignment -> ROSTER FULL retry-loop for the
whole 90s window (found by verification round 2).
6. **Beacon NAT keepalive**: SIO_KEEPALIVE_VALS 60s/10s on the seat beacon -- long
between-rounds waits can't be silently unseated by NAT idle timeout. [T3 -- not
NAT-tested]
VERIFIED NON-ISSUES: relay-mode pods never bind the -net console listener (early dial-out
return @L4NET~900) -- no double-launch port collision on the internet path; mesh bind-fail
exits cleanly via PostQuitMessage. TOOLING: `BT_LOG_APPEND=1` (btl4main) preserves the log
across lobby-loop generations -- REQUIRED for multi-generation forensics (truncation erased
the evidence in verification round 1).
- **LOBBY LOOP + SEAT RECLAIM (2026-07-23) [T2]**: the between-rounds arcade flow. Field
problem (first playtest night): at mission end every pod EXITED -> seats dropped -> every
round required everyone to re-run join.bat, and the operator could not adjust the mission.