docs/testing/vuh2884-native-terminal/README.md

Mansion terminal ordering candidate (VUH-2884)

Code: 9e6b2e6, based on origin/main 9d36a74, branch vuh2884-mansion-death-race. Historical source runs used bc8d9ad. This is an offline candidate, not a live acceptance result. Juno released the PC; a direct serial, below-normal-priority CMake build is authorized. No game, desk, Steam, install or save action was taken.

Observed failures

Source root (read-only): C:/Users/volpe/repos/kh2-multiplayer/.local/wt/vuh2466/build/scenarios/. Line numbers below are one-based in the original inject logs.

RunSourceDecisive sequence
20261026-193245_net_prologue_mansion_hall_1host 1571202343: spawn5/netId6 disappears at HP44; 2410: sent as left=1; 2424: native mission-complete, frame6617; census closes; 2456: spawn4/netId5 disappears at its retained HP16. No native funnel receipt for either wave-two body.
samefriend 1883962665: receives netId6 as leave; 2694, 2707: end defers with two living bodies. No netId5 death.
20261026-194057_net_prologue_mansion_hall_host_down_1host 238900, friend 162672Host records and publishes all six native deaths before its end. Friend applies all six, then enters its own native end event. At 2870 it releases following; its census is empty. No replay log; the old observer only reports Bailey's native-local end.
20261026-194738_net_prologue_mansion_hall_friend_down_1host 999242619: final netId5 funnel kill; 2620: mission-complete closes gameplay; 2673: death finally published after the event.
samefriend 236068End defers while netId5 is HP7, then does replay, 3167, ok=1 path=frame-cap. Death arrives 3180, applies 3208, empty census 3209. The 64 deferrals are not evidence of an empty roster blocking.
20261026-180147_net_seat_activation_calling_1host 2338042326: group-one Dusk also sent as despawned, not refilled, left=1.
20261026-200433_net_prologue_mansion_hall_host_down_1host 100968, friend 1792722340: netId5 disappears at HP32; 2416: leave=1. NetId6 remains at HP31 until the full-party game over at frame7155 (2605/2606), then disappears (2659). No host mission-complete. This disproves attributing every wave-two failure to the mission-ending kill.
20261026-201309_net_prologue_mansion_hall_friend_down_1host 156668, friend 89568NetId5 disappears at HP43 (2138), publishes leave=1 (2200); mission-complete at 2202; netId6 disappears after the hold at HP32 (2256), with no death. Friend replays through the cap (2619).

The base log proves absence won over death accounting; it does not identify the missing native lethal caller. Dusks also have real alive removals. Neither absence, a positive last HP, nor mission-complete alone licenses a synthetic kill.

Candidate

  • Reuse the byte-verified 3D4A40 lethal-notification detour as a default-on observer. Diagnostic hooks/storage remain opt-in. On normal return, require a newly set native dead flag and unchanged actor, objentry, status, controller, record, object ID, announced occurrence and complete session/load/room scope. Foreign calls, unavailable reads, native unwind and changed identities refuse.
  • Retain a copied terminal receipt separately from sampled HP. The HP funnel remains a second source. Pending real deaths drain before mission snapshots, including gameplay holds, without dereferencing cached actors. Failed enqueue retains the death and holds back that mission publication.
  • Reconcile client deaths before mission replay. The readiness check takes a fresh census and accepts empty or all-dead rosters; unavailable or living rosters defer. Record native-local hall completion once as well as replayed completion, bound to the retained admitted host mission-complete occurrence even after a later inactive snapshot. The three Mansion scenarios now require exactly one replayed or host-correlated native-local friend completion; a missing friend end no longer passes <= 1. The existing fallback cap remains.

Offline controls

kh2coop_mission_mirror_test covers changed scope/roots, duplicate or unavailable death edges, pending publication and empty/settled/living/unavailable rosters. It replays exact selected host-log lines in tests/fixtures/mansion_terminal/; sources.json pins each original log. Run 194738 exercises drain of an actually observed final funnel kill during the hold before mission publication. The base trace deliberately expects only four observed native receipts: it never manufactures the two missing kills.

kh2coop_nativehit_test exercises the actual EnemySync adapter over owned memory: positive-HP native terminal capture, changed record/load refusal, enqueue refusal and retry, the held owner frame publishing a death before mission state, no duplicate publication, client death before clean-census end, and a native-local end bound to a retained admitted host completion. An independent local end, wrong load and end admitted during travel earn no receipt.

kh2coop_lifecycletrace_test exercises the real detour wrapper with synthetic native functions: exactly one native invocation, diagnostics off, LastError preservation, duplicate notification, changed scope, unregistered caller and propagated native exception without completion. It installs no hooks and opens no game, bridge or process.

Result

  • Full serial Release build passed on 9e6b2e6 / 9d36a74.
  • Serial CTest: 277/277 passed, 264.87 seconds, including the actual adapter, detour, log-excerpt controls and the updated wave-order scenario tests.
  • All three Mansion scenarios passed offline structure/expression validation.
  • Verification receipt is attached to VUH-2884. It pins the code, fixture provenance, build/test logs and staged DLL. Local logs: .local/build-rebased-vuh2884.log, .local/ctest-rebased-vuh2884.log, .local/scenario-validation-vuh2884.log.

Independent review and the lead's live reruns remain: base, host-down and friend-down must each prove 6/6 actual deaths applied and one mission end settled before room exit. Native-notification coverage of the missing historical wave-two bodies remains unverified. No live run or Thorn victory testing occurred.

Review correction (Nora, 2026-10-26)

The three completion predicates previously counted the native replay log prefix when its result was ok=0. They now count only ok=1 replay receipts, alongside the existing host-correlated native-local receipt, still requiring exactly one. The production runner Context tests exercise all three committed predicates: failed and missing receipts fail; successful replay and native-local completion pass; duplicate replay and replay plus native-local completion fail.

Delta checks: 16/16 Python tests passed, and all three scenario JSONs passed offline validation. This changes scenario predicates/tests only; the previously verified DLL is unchanged. No new live evidence is claimed.