Scenarios (VUH-1488)
A scenario is a JSON script that the runner plays against rig-launched KH2 instances: launch → load the save → warp → inputs → waits → memory assertions → captures. Every run writes a report with its artifacts. A crash or hang ends the run with a bundle instead of stalling it. Scenarios are the regression suite for live behavior, and their reports are the evidence an issue closes with.
python tools/scenario/run.py tools/scenario/scenarios/boot_to_goa.json
python tools/scenario/run.py tools/scenario/scenarios/*.json --repeat 5
python tools/scenario/run.py tools/scenario/scenarios/net_host_transitions_smoke.json --validate
Requires the DLL and kh2ctl built (cmake --build build --config Release)
and the desktop session (KH2 crashes at startup from session 0).
--validate only parses JSON, checks supported steps, required fields and
instance references, and compiles expression syntax. It does not evaluate
expressions, acquire the rig lock, inspect processes or saves, or launch
anything. It does not establish live behavior or validate arbitrary CLI args.
For tight diagnostic timing on this Windows Python runtime, qualify durations
with time.perf_counter_ns() (QueryPerformanceCounter). Keep native/monotonic
timestamps as separate metadata and never compare clock domains. In endpoint
control 125253, a single 16.66-second sleep measured 16.656 seconds on the
coarse monotonic clock; the strict guard refused the end read. That FAIL remains
intact. Fresh control 131241 measured 16.661754 seconds with QPC and qualified
both endpoints under the same bounds, without adding polling or a tolerance.
See the clock correction and live result.
Enemy hash notice debounce (VUH-2212, VUH-2270)
Enemy-only hash notices (fields=2) require three counted client samples on
the same epoch. This debounces the observed damage-then-death ordering against
the relay's cached host hash; agreement resets the streak. Room, progress and
complete-missing-pack notices retain their two-sample threshold. Hashes still
use native current HP, not the host's cached HP or max HP.
VUH-2270: a client sample counts toward an enemy notice only when:
- the host hash is fresh (at most two client samples since the host's latest);
- lag doesn't explain the mismatch (the client isn't showing one of the host's last four hashes and didn't show the host's hash one sample ago);
- with equal or unknown populations, the host's last two hashes are equal (settled);
- with complete, unequal populations, the same lag test passes on the living counts.
Other samples are skipped, without counting or resetting. This stops the false
notices from staggered HP sampling in active combat (TT 02/0D, run
20261014-205117) and from friends applying deaths while the host's census is
silent during a boss event (BC 05/0B, run 20261014-203940). Persistent
HP divergence reports once the host settles, and a missing pack still reports
while the host's HP changes. A client about 2 s behind with HP the host never
sampled can still report.
Automatic reconnect: one cycle (2026-10-04)
Run 20261004-213052 passed in 201 seconds with protocol 10. The host-outside setup was unchanged. Friend1's complete empty combat publications join through two distinct relay receives, comparisons and alarm to exactly one automatic request. No setup or manual request occurred. Friend1 loaded once for rejoin and once for Bootstrap; the other two games did not reload. Claims stayed held until all five original IDs matched HP17/max20. Final censuses were 5/5/5 with no extras/deaths, and the pack persisted through 120 live updates. All four protected saves were unchanged. See the bounded result and Friend1 capture.
Run 210134 remains FAIL: an HP-lag setup difference caused a false room reload. Protocol 10 limits automatic requests to complete absent living packs and counts only fresh client observations. Run 212553 remains FAIL: the game recovered, but the evidence reader mishandled continuously arriving unsealed log tails. The corrected one-cycle reader retains those tails and certifies through newer real post-census seals. Neither result qualifies the parked ten-cycle gates.
Private Tailscale transport: one real game (2026-10-04)
Run 20261004-214513
passed with a relay bound only to 100.108.214.60:27795, one real Windows game
and a synthetic Mac Friend1. Mac run pc-leg-02 stayed connected for120 seconds,
sent7,253 avatars and received5,660 actual PC poses with zero errors. On the PC,
49 bridge/native samples showed the Mac-controlled Donald moving across590 units;
mean error to a bracketing bridge pose was4.4 units. Renderer start/end captures
show the movement. All four protected saves were unchanged, owned processes
stopped and port27795 was free afterward.
The bounded result retains the Mac receipts, sampled/non-atomic measurement limits and earlier config/sample failures. This validates private avatar transport with one game. Two-Windows-game combat/progress and the friend playtest remain untested.
Two real games through a Mac relay (2026-10-04)
James has no friend lined up yet. The rig ran two real KH2 games on this PC,
scripted each native player and connected both to the actual relay on the Mac
at 100.103.220.58:27795. The relay used protocol 10, a Tailscale-only bind and
no fake simulation flag. Existing reviewed product binaries were unchanged.
Runs 20261004-221407 and 20261004-221645 both passed in 96.8 seconds. Normal movement had 286/285 matched native puppet samples with mean errors 0.95/1.47 units. Configured delay/loss movement had 280/279 samples with mean errors 1.34/2.53 units; both runtimes logged 50 ms delay, 10 ms jitter and 2% loss. Each native player travelled over 1,200 units. The impaired run's fresh Mac checkpoint confirmed both active player slots and the sole private listener. All four protected saves stayed unchanged in both runs.
This is an automated movement/network check with two games on one Windows PC, not a human friend playtest, second-installation check or remote combat/progress validation. The lag-compensated sampled errors are not latency measurements. See the result and rerun.
Historical activation replay: single cycle (2026-10-04)
net_reconnect_shadows_activation_replay.json is the current 98-step bounded
fixture: the93-step population derivative plus five post-replay hold checks.
It requires both KH2COOP_SURVIVING_PACK_PREPARE=1 and
KH2COOP_SPAWN_TRACE=1. The original repeated-reconnect fixture and strict
ten-cycle acceptance remain separate and unchanged. The baseline
reconnect_mark observation supports a15s bound; extending that observation is
not recovery and does not relax population, HP or identity checks.
The default-off implementation uses the actual host first-emission float4 for one exact all-alive five-Shadow controller through its normal scheduled update. It adds no native call or geometry/flag/cache/count write. Preconditions include the recorded input/definition/incarnation binding, zero deaths/tombstones and a fresh client empty cache/census with counts5/5, flags2, cooldown0 and stage0. The host's observed header+E0 may match client+E0/1 under the reviewed explicit exception, with both raw values retained and no marker rewrite. See FORCED_RESYNC.md for the HARP/v1 trailer, initialization binding and refusal rules.
Historical input permits at most512 actually completed selected-controller original updates, under the same30s deadline. Selected pending/deferred progress may wait; positive conflicts fail. Hold selected claims until the whole exact set has reconciled HP. Then restore live host input and require120 actual selected-controller completions, continued exact5/5/5 membership at HP17/max20, no extras/deaths/tombstones and matching final controller state. The64-slot per-controller completion table never evicts and fails unavailable on overflow; global sequence differences and lease-copy attempts are not counts. A timeout after partial emission stays a partial failure, not a retry.
| Attempt | Result | Scope |
|---|---|---|
| 20261004-132438 | FAIL at step81 reconnect_mark,204.2s | Failed before reconnect; all four protected saves unchanged. |
| 20261004-133512 | FAIL at step81 reconnect_mark,200.9s | Failed before reconnect; all four protected saves unchanged. |
| 20261004-134727 | FAIL at added step93 replay-verification wait,224.4s | Original93 steps passed, both post-rejoin censuses5/5/5. No ResyncPlan or replay ran; all four saves unchanged. |
| 20261004-140622 | FAIL outside control at step111,234.4s | First sample5/0/5 outside; second5/5/5 after host entered BOX0. The outside precondition broke; this does not falsify the diagnosis. Four saves unchanged. |
| 20261004-142007 | FAIL final assertion with KeyError: runnerBindings,235.3s | Both complete post-rejoin samples5/0/5 and all seven BOXes outside; native reconnect audit ready. The fixture asked for runner fields absent from reconnect_check. Final death/no-replay checks were not executed; four saves unchanged. |
| 20261004-143318 | PASS all120 control steps,242.1s | Two complete5/0/5 samples with host endpoints outside all seven BOXes, original survivor HP17/max20, exact original identity/load/progress and zero-death/no-replay checks. Four saves and98 sealed inputs unchanged. |
| 20261004-143903 | PASS all142 forced-treatment steps,259.3s | Qualified5/0/5 prefix, one slot1 Bootstrap, actual recorded host point,33 completed historical updates and120 completed live-input updates, reconciled1 and two complete5/5/5 HP17/max20 samples. Four saves and98 sealed inputs unchanged. Forced scope only. |
| 20261004-155707 | FAIL outside movement at step92,206.5s | On commitf10b0d4 with automatic recovery explicitly disabled, native finishZ365.722 missed the unchanged450<Z<600 band. Failed before Friend1 pause/rejoin or the forced request; recovery was not exercised. Four saves unchanged; owned cleanup completed. |
| 20261004-192433 | FAIL movement budget at step88,236.3s | Candidate02 admitted12 pulses in7.391s before its8s budget guard refused another pulse/settle. Last observedZ565.096 was inside the acceptance band but above stop550; final700ms settle/read and geometry/reconnect gates did not run. Four saves and all35 pins unchanged; rig idle. |
| 20261004-194715 | FAIL third boot at step2,105.6s | Candidate03's helper was never exercised. CLI's15s hook wait expired; later log completed initialization. Failed PID795000 was outside the runner's cleanup list and was killed through exact owned kh2ctl kill --pid. Four saves and all47 pins unchanged; rig idle afterward. |
| 20261004-200302 | FAIL pre-pause clearance at step104,250.3s | Candidate04 booted with45s hook waits; its12-pulse/4.266s helper settled at(39.2959,-1,520.245), and initial sampled clearance261.516 passed. Later pre-pause host(-170.799,-1,667.840) had142.698 clearance, below200. Friend1 pause/rejoin and recovery never ran. Four saves/all43 pins unchanged; owned cleanup completed. |
| 20261004-205359 | PASS forced regression,303.9s | Moving full diagnostics before the walk kept the original fresh population/geometry checks and 200-unit clearance. Both post-walk census groups passed in 7.80s and 8.08s. Friend1 rejoined without its pack; one forced Bootstrap restored five original Shadows on all peers at HP17/max20, with 120 live updates verified. Four saves unchanged. Automatic recovery was disabled. |
These are observer/setup failures, not new recovery failures or acceptance. The observer validator's hardcoded WorldBridge10 check was inconsistent with current WorldBridge11. The exact version correction is integrated;36 offline evidence/ runner controls and22 canonical evidence tests pass. The intact saved peer0 observation now qualifies individually, while both incomplete three-peer artifacts remain rejected. Run134727 then passed baseline collection and all original checks; neither older failed run is retroactively passed.
The replay fixture and its repeat candidate remain held pending automatic-route qualification. In run134727, before notice integration, cached-world rejoin did not start the explicit ResyncPlan carrying historical metadata; the configured native opt-ins produced no resync-activation receipt. The separate control uses actual native host movement outside all seven current BOXes and requires5/0/5 after Friend1's fresh rejoin. Its complete PASS qualified the separately labeled slot1 forced-resync experiment below. Automatic live qualification and both original/forced-outside ten-cycle gates remain open. The farther control uses the same bounded native movement toward Z500, with observed clearance before rejoin. Run142007 remains FAIL; its schema correction uses returned peer PIDs plus the producer's unchanged mandatory process/argv/ relay guards. Run143318 subsequently passed the complete control. Run143903 preserved that exact120-step prefix and exercised one canonical host request for Friend1. It accounts for exactly one additional Friend1 load4->5 and transition3->4, with original processes/session/pins/roster and unchanged survivor loads. Native ACK, relay/runtime terminal evidence, full three-peer hashes and independent native censuses agree. Host point bits join the receiver by firstUpdate55413; client raw point bits and canonical snapshot bytes are not independently retained. Measured endpoints do not prove continuous outside state. This is bounded forced recovery, not automatic or ten-cycle acceptance.
The unchanged forced fixture then failed its outside movement on the new commitf10b0d4 in run155707, before reconnect or treatment. Its250ms probe underestimated the continuation rate, so the single1929ms native pulse overshot the original lower Z bound. This setup FAIL is retained; it does not qualify or falsify the recovery code. A distinct private candidate replaces only that long extrapolation with same-direction native pulse/read feedback, at most24 pulses within one8s deadline. The original final position, full location, all-seven-BOX, census, HP, progress and death gates remain. No transform write, reverse rescue or retry is allowed. Candidate02 exhausted that8s budget in192433. Candidate03 parallelizes only four independent read-only observations; its independent concurrency controls pass, but194715 failed during boot before the helper. Both FAILs remain unchanged. The next distinct qualification uses the same24-pulse limit, band,700ms settle and geometry checks with a20s setup walk budget and45s hook wait. These are harness time bounds, not recovery acceptance criteria. Run200302 qualified the helper's settled endpoint and immediate geometry, then failed the later200-unit clearance assertion before Friend1 pause/rejoin. The host drifted about256 units across intervening observations with no scripted movement step. The two endpoints establish the drift, not its cause or continuous clearance; diagnosis precedes a new attempt.
Run205359 passed the reviewed walk-last census split on its first live attempt. The full provenance and occupancy diagnostics now run before movement; the four fresh post-walk population/geometry samples and all original assertions remain. The shorter observation path kept the host outside through the reconnect and forced recovery. Station-keeping was unnecessary in this attempt. Use this deterministic forced route for routine regression; the automatic route is a separate one-cycle qualification. Both ten-cycle gates remain parked by the 2026-10-05 lead brief.
The integrated host notice route remains default-off and requires exact 1 for
KH2COOP_AUTOMATIC_RECOVERY, KH2COOP_SURVIVING_PACK_PREPARE and
KH2COOP_SPAWN_TRACE. It uses the existing request generator, combined requested/
plan busy state, captured-context dedupe/coalescing and two pending friend slots.
Only a terminal permits pending drain, with full context and runtime ownership
revalidation; there is no retry or deadline reset. The always-on generic client
generation hold suppresses all outgoing client claims until a complete admitted
living manifest has unique full native census coverage and exact actual HP/maxHP
readback. Empty/unknown universes and incomplete matches stay held.
Actual request-key, claim/replay, runtime reset and native queue/issue/load/arrival receipts now support episode attribution. A valid interval needs complete raw coverage and producer seals after the final census; read the runtime watermark after that census, then require a newer complete seal and no pending/requested/ planned work. Shutdown is unnecessary, and cross-process clocks are not compared. Unavailable ordinary native session identity must join independent runtime/relay evidence. The new opt-in causal diagnostics add actual native publication, relay compare/notice, all request origins, cached-send outcomes and actual envelope/body admission. Join the same recovering slot/connection throughout and reject terminal or changed final binding evidence. The original private consumer's fail-open cases remain BLOCKED; its corrected copy and prospective collector still need qualification. These receipts alone do not qualify an episode or relax v1's existing no-forced-resync rules. James approved the separate v2 natural/resynced contract on2026-10-04 (VUH-1508 comment7ca6ae56): the original-route gate requires10/10 passing cycles in any natural/resynced mix; the outside gate requires10 resynced cycles. A natural outside cycle fails without a skip or retry. Every other classification fails. Both live ten-cycle gates remain unverified.
The full Release build and 2,298 affected offline checks pass, including 59 notice/generator, 44 claim-hold, 66 load-receipt and 365 canonical native-hit checks. The combined receipt retains executed results; private sanitizer checks are scoped separately. The first combined build failed on a test adapter declaration; the corrected explicit-argument calls retain all assertions and pass the final build/run.
The earlier first-rejoin5/0/5 failure and zero completed ten-cycle acceptance
remain. General deaths/waves, barrier/music/room-script parity and physical
remote recovery are unqualified. List unavailable battle/event fields explicitly;
the location's btl value alone is not battle-state parity. The older B1 creator,
loader-installation and spatial-bypass work below is parked research for an
added-dispatch alternative, not a prerequisite for this scheduled-update policy.
HUD peer labels: offline implementation (2026-10-06)
Build and run the retained CPU regressions without a game:
cmake --build build --config Release --target kh2coop_hud_names_test kh2coop_hud_cpu_test
./build/Release/kh2coop_hud_names_test.exe
./build/Release/kh2coop_hud_cpu_test.exe
The name controls check admitted-session/connection/generation binding, owner-row projection, malformed and colliding ASCII labels, retirement, the 1,000 ms expiry, partial publication, sequence exhaustion and concurrent copied reads. The exact name test passed 62 checks normally and under Windows ASan; existing HUD controls passed 52 ASan checks. The matched private DLL/runtime/avatarctl Release build uses AvatarBridge 3 and protocol 10. These are offline results, with no deploy.
A fresh live names run must use matching v3 readers and distinct short peer IDs,
show both names on their owner HP rows in both games, retain Host/You and RTT/loss,
verify departure/reset and stopped-runtime expiry, and inspect readable captures.
The accepted 073856 run proves owner HP and departure only; it does not validate
these new labels. See the source review
and matched build.
Native record-content resync: offline candidate (2026-10-04)
This dated candidate used protocol 9, AvatarBridge 2, WorldBridge 11 and CaptureChannel 1. The dated protocol-8 results below remain historical evidence for their exact binaries and fixtures; they do not validate this new candidate. All components must use compatible rebuilt versions. The new bounded living-population native result is recorded below; the broader reconnect and creation acceptance gates remain open.
Actual native snapshots now require coverage 255, a complete bounded ordinary-controller catalog with full 44-byte headers/all ordered 64-byte records, and checked current actor-to-record membership. Exact byte comparison excludes only header +0xE for peer compatibility; the raw byte remains evidence. Synthetic coverage127 stays supported by the generic codec and is rejected by the actual native consumer. Missing, ambiguous, unsupported or partial native associations cannot fall back to matching object/point/manifest index while fenced. Whole-current-catalog and population qualification precedes the first HP/death write, with positive/max bounds for every sampled native and desired living HP, plus repeated selected bytes/state and a final context endpoint.
Waiting holds writes; Exact is an exact-only strategy that can await natural population recovery at the unchanged deadline; terminal Failed survives result cleanup. Checkpoint is observation-only and performs no HP/death repair, progress write or second reload. Changes after preflight can still leave partial application. Unknown lethal outcomes or failed post-call reads/censuses remain Failed without replay. Neither sampled equality nor repeated pointer values prove atomicity, controller lifetime, incarnation or pending-work exclusion.
Actual offline Release and Windows MSVC ASan executions each passed:
| Component | Executed checks | Receipts |
|---|---|---|
| Common record comparison | 154 | Reader/common final receipt |
| Production native reader/trace harness | 276 | Reader final receipt |
| Wire/transaction harness | 257 | Release, Windows ASan |
| Production consumer and write guards | 272 | Final guard receipt |
Counts include repeated setup checks and are not independent scenario counts.
Independent common,
reader
and consumer fix
reviews are READY within their offline scope. Initial HOLDs, opaque test aborts,
the source-serial fixture failure and intermediate 233/269-check results remain
preserved. Final guard source SHA256, before receipt-only instrumentation, is
8E7BBFF246379030578793C42569B45F1E134960C9CC95D2BA3034E77E685F03.
See FORCED_RESYNC.md
for exact behavior, limits and evidence lineage.
Bounded native result, 2026-10-04: the unchanged fixture
net_forced_resync_shadows_population.json passed all 89 steps in 201.7 s,
20261004-043836.
One both-target Bootstrap preserves the original connections/delivery identities;
Friend1 and Friend2 load3 to4 and return checks63/count5/dead0 on distinct frames
6100/6101 and3633/3634, with matching actual relay and host-runtime terminals.
The host's full coverage255 catalog contains10 definitions/26 records/five refs.
Both targets have five unique native20-to-requested17 store returns, followed
by actual HP17/max20 on both content-qualified observation frames. Immediate
store readback and store frames/loads are not logged. Two fresh matching hash
samples and two complete independent native censuses retain all original
bindings1-5/object302/type4/damagedHP17 and progress; no death history appears.
Content
and population/capture
audits are READY within that scope. Current EnemySync source is
04298BAB3B948BC1E04F3E21F5D18D442D3638F5BCB0F6BBBF01F7C3D54F17AB;
all101 inputs/four protected saves match through terminal verification, all8
owned processes exited and the rig is idle. The full build, eight affected
regression suites and receipt-instrumented272 Release/Windows ASan controls pass.
Three intact inspected1920x1080 final PNGs show rendered courtyard gameplay, with overlap/occlusion; count and HP rely on the checked native rows. Post-resync geometryComplete=false and positive geometric stability is unclaimed. Progress apply has zero changed spans/bytes and only an apply-boundary personal witness. Automatic report1 is labeled complete but aggregate collection is partial (one cadence suppression). The prior first-rejoin population FAIL, zero completed ten-cycle repetitions and disabled creation remain. This result does not close absent surviving-set reconstruction, lifetime/creator/pending closure, dead recovery, natural/personal progress, full-route or physical remote acceptance. Historical protocol8 results below remain unchanged.
Natural-resource diagnostic, 2026-10-04: the same89-step fixture passed in
203.8s with PREPARE, SPAWN_TRACE and NATURAL_RESOURCE_TRACE enabled:
20261004-080445.
All173 inputs and four protected saves matched at terminal verification. The
three byte-qualified/pinned package observers retain25 full selected-definition
parent matches and270 exact child joins across the first selected pack on each
peer. Each friend suppresses235 later child receipts after the512 budget cap;
later reload child absence is unqualified. Exact original IDs1-5/HP17/max20,
load3-to4, ACK63 and matching relay/runtime terminals pass. Collection remains
partial, positive point stability unclaimed, and only the runner has a retained
OS exit code. All eight recorded PIDs are absent and the rig/index are idle.
This is bounded natural Bootstrap/observer validation; first-rejoin FAIL,
zero completed ten-cycle repetitions and disabled added creation remain.
See fixture/lifecycle audit
and full-byte parent joins.
Fresh forced resync: implementation candidate (2026-10-03)
The then-current protocol 8 / AvatarBridge 2 / WorldBridge 10 /
CaptureChannel 1 candidate implements fresh checked host capture, immutable
targets, delivery/source fencing, complete staged bootstrap and distinct native
convergence ACKs. FORCED_RESYNC.md defines the fixed deadline,
one non-reloading Checkpoint and explicit unavailable outcomes. The verified
offline receipt is build/rig/resync_offline_receipt_20261003.json: Release and
Windows ASan each passed 1,897 C++ checks, portable ASan+UBSan 1,340, with no
sanitizer findings. Each includes 205 codec/ENet resync controls; Windows adds
185 production native controls over owned memory. Nine existing linkage and
24 saved-evidence cases, three actual PID-0 runtime smokes and the new scenario
structure check passed. Synthetic native facts remain labeled.
net_forced_resync_smoke.json prepares one host command targeting both original
friends in a complete empty GoA room. It requires correlated native and relay /
host-runtime results, changed loads, two native observation frames, then two
fresh independent hash/census joins. It does not establish nonempty, dead or
remote acceptance. Native execution remains separate from the offline receipt.
Its current local execution 20261003-131544_net_forced_resync_smoke_1 passed all
18 steps in 149.2 seconds: both friends loaded serial 3 to 4, produced distinct
native-frame Converged ACKs, and matched both terminal results and two complete
post-resync empty census/hash samples. Saves were unchanged. Progress apply
changed zero bytes; nonempty/dead/changed-flag recovery remains open. The first
attempt is retained as a failure at obsolete readiness log text; the runner now
waits for verified membership while native bootstrap checks remain separate.
net_forced_resync_outsider_seatstate.json (VUH-2675, D14) is the same empty-GoA
smoke with shipped player defaults and a single-target resync of Friend1 only.
Friend2 sits outside it and must keep admitting Friend1's traffic:
- Before the resync, Friend2 and the host each log at least three
[prologue] seat accepted owner=1, and no runtime has logged a peer serial move. - After the resync, Friend2's runtime logs exactly one
[Runtime] peer delivery slot=1 serial=2. Delivery serials start at 1 and only a resync plan moves them, so 2 is this resync. Neither the host nor Friend1 logs one: they take the plan itself. - The admission baselines are taken only after Friend2's notice and the outsider-only check are verified. After that, and after the smoke's own convergence evidence, Friend2's DLL accepts at least three more owner=1 seats. The host's owner=1 and Friend1's owner=2 seats are the controls. Friend2 logs no StageEnd envelope drop.
- After the captures, the run repeats the exactly-one and outsider-only serial checks, so a later raise also fails.
It does not exercise StageEnd, because there is no event in this room. It cannot
show Friend2 refusing an old-serial envelope: the relay refuses a stale source
serial first (source-delivery), so that clause stays with ForcedResyncTest
outsiderAdmitsRecoveredSeat. Not run yet.
net_forced_resync_native_nonempty.json retains a separate living-only first-pack
fixture with host-only damage and exact typed native population checks. Its first
run 20261003-132801 failed before requesting resync: current four actors matched
at HP 153, but two earlier positive-last-HP bindings had disappeared and received
transport tombstones. The zero-history gate rejected this setup. This is a
retained prerequisite failure, not a native recovery result or proven lethal
death. The original fixture remains strict and unchanged.
net_forced_resync_first_activation.json is a distinct setup with one native
900-ms pulse per peer and the same original zero-history baseline. Run
20261003-133716 failed at population readiness: all peers stayed empty, before
damage or the command. Its saved evidence and strict expectations remain. Native
activation geometry must establish the next setup; pulse count alone does not.
net_forced_resync_native_shadows.json uses measured courtyard activation. Run
20261003-135207 produced five object-302 Shadows at HP 20 with complete native
joins on all peers, then failed positive host-point stability before damage or
resync. Its 90-step source and failed evidence remain unchanged.
The separate 89-step net_forced_resync_shadows_population.json removes only
that positive geometric acceptance assertion and makes no positional stability
claim. All raw collection, negative authority controls, complete population/HP
checks, original zero-death history and recovery requirements remain identical.
Run 20261003-140717 passed in 190.4 seconds: exact HP 20 to 17 on all five
original Shadows, one both-target command, genuine loads 3 to 4, distinct native
Converged frames, matching snapshot/cut/fingerprint and relay/host-runtime
terminals, then two fresh complete native census/hash joins at HP 17. Three
final game captures were saved and four protected saves were unchanged.
This is local living-only Bootstrap convergence. Clients already matched before
the request, and forced progress application wrote zero bytes. Deliberate
stale-client repair, changed progress, dead reconstruction, Checkpoint, physical
remote recovery and positive position stability remain open.
net_forced_resync_shadows_progress.json replaces only that fixture's command
at index 79 with progress_fault_resync; all other 88 steps remain identical.
Run 20261003-145341 failed its unchanged negative geometry guard before any
fault or command. A single unchanged retry 20261003-152248 passed all 89 steps
in 187.9 seconds. Full raw snapshots retained Friend1's sole shared byte fault
00 to 02, two fresh progress-only mismatch samples and one exact both-target
transaction. An ordered native apply changed one span/byte back to 00 before
the additional loads 3 to 4; complete postchecks restored the original full
masked progress and five Shadow HP17 bindings. Cleanup performed no write.
Four protected saves stayed unchanged. This proves one injected shared-bit
repair locally, with non-atomic context/read limits; it does not prove natural
progression, stale HP, dead history or physical remote repair. Apply-level
personal equality stays separate from post-load differences (both clients'
Goofy current MP at SAVE+0x271E changed from 100 to 90; sampled HP stayed28/28
and maxMP100. OpenKH's FM character layout identifies this as record2,+6.
The causal writer and exact write time remain unestablished. The earlier frozen
receipt's HP label is an error; preserve that artifact and its original bytes).
The three final capture steps now retain their actual CLI completion receipts alongside the PNG paths. In this run, each exact PID's independent mailbox reported request/done 36, status 0, one frame, renderer 12, format 87 and 1920x1080. Equal sequence numbers do not mean synchronized game frames. Earlier reports lacking these fields remain unchanged; no receipt was backfilled.
The operator command uses an existing admitted host mapping and a separate CAS
mailbox; queued:true is not relay or native success. Cancellation unarms native
world authority, and lazy attachment preserves bounded post-cut continuation.
The relay defaults to native traffic; legacy --simulate sessions cannot supply
native capture. Dead-spawn identity, nonempty native reconstruction and remote
acceptance remain open. Frozen results below retain their original versions.
Same-process native Friend1 reconnect candidate (2026-10-03)
net_reconnect_shadows_population.json preserves the population fixture's first
79 step objects. After two fresh native/hash observations establish the original
five Shadows at HP20/maxHP20 and host-only damage3 leaves HP17, it seals a
three-peer reconnect baseline and interrupts only the original owned Friend1
runtime. The game, host, Friend2 and relay stay alive. The runtime is resumed
after fresh relay retirement and both survivors' matching roster observations;
a watchdog/finally recovery bounds the interruption to 20 seconds on a working
OS/API. No runtime restart, host restart or forced-resync fallback is used.
The strict checkpoint requires intact raw log prefixes, the same original processes/session/host/epoch/full location, a fresh Friend1 connection and generation, actual typed close/retry/admission, and ordered new native progress/apply/issue/load/arrival/activation evidence. Current puppet provenance must join distinct active native targets with finite poses and bounded error. Two fresh population/hash samples and two complete native censuses separately require the original IDs/object302/HP17/maxHP20 and unchanged progress. A second read-only reconnect check after those censuses catches survivor reloads during that window, before the original zero-death guard and three renderer captures.
The 93-step fixture passed in 193.5 seconds on the local Session-1 rig
(20261003-173002). Its actual pause/resume returned native status0 once each,
with elapsed5.062s and no deadline or emergency cleanup. The original Friend1
runtime retried after a typed timeout: connection2 became4, generation2 became5,
and native load3 became4 under the original session/host/epoch. Host/Friend2
loads stayed2/3. Both recovery checkpoints replay from 21 independently checked
raw log receipts; three independent reviews accepted identity, lifecycle and
population evidence. Five original IDs/object302 retained HP17/maxHP20 and
progress9AC5499B in fresh complete samples, with zero deaths/tombstones. All
three final PNGs have exact PID/mailbox completion receipts; four saves were
unchanged. Automatic diagnostics retain a complete original-roster report and
two cadence-suppressed triggers, so aggregate collection is partial.
The offline prerequisites passed123 Python controls, full CLI structure validation and an actual native suspend/resume adapter smoke on a created throwaway process. The initial older fake-context compatibility failure remains retained; that fixture now has the real owned-process registry and three duplicate-launch controls. Exact raw sample bytes and partial failed observations are retained for replay. Full log reads, observations and captures remain separately sampled, not globally atomic. This single living-only loopback result does not establish repeatability, general/dead reconstruction, stable-point geometry or remote recovery. A fresh process launch would test readmission rather than this same-process automatic recovery path.
The separate ten-cycle net_reconnect_shadows_repeat.json retains every
population/progress/death gate and the original host/session throughout.
Run 20261003-175046 failed after352.6s at step161, cycle06's statehash check.
Cycles01-05 have complete recovery, population/census and final evidence.
Cycle06 rejoined as connection9, generation20, native load9/transition8, but
Friend1 stayed empty while Host/Friend2 retained the original five HP17 Shadows.
All three still matched shared progress9AC5499B. Cycles07-10 were not reached;
five passing prefixes do not establish ten-cycle acceptance.
Independent review replayed119 raw receipts, six baselines/after checkpoints,
five final checkpoints and six retirement proofs. Complete native tails show
zero survivor reloads and six Friend1 reloads. All four saves stayed unchanged;
the owned instances exited and the rig is idle. Full failure evidence and frozen
inputs are retained in build/rig/reconnect-repeat-package-20261003.
Cycle06 logged30 lease points outside all seven region boxes from cycle05's saved geometry, with32 subsequent empty native hashes and no local spawn/match lines. This is a strong activation-geometry hypothesis, not proven cause: cycle06's own geometry was not collected before the hash gate failed. That failure required read-only current-load geometry/controller/native census before the unchanged gate in a separate ten-cycle diagnostic. Keep all original guards; do not reposition the host, shorten the run or use forced recovery to turn the retained failure into a pass. Controller records/apply counters alone do not establish materialized native actors.
net_reconnect_shadows_repeat_diagnostic.json is the separately named collection
derivative: all230 original step objects remain in order, plus20 all-peer native
censuses (two samples1200ms apart before pause and after rejoin per cycle) and
ten additional post-census identity checks against each cycle's original mark.
It retains empty native rows, current region/controller bytes and exact accessor
position readbacks; collector completeness is separate from geometry coverage.
The added reads change timing, so a different failing cycle is possible.
Run 20261003-182709 failed after358.0s at step143, cycle04's unchanged hash
gate, after three complete cycles. Fourth-cycle before-pause native counts were
5/5/5, including Friend1 load6/transition5, while the host was already outside
all seven boxes. After actual rejoin, connection7/generation14/load7/transition6
passed both original and added identity checks, but two complete independent
current-load censuses had counts5/0/5. All48 added peer snapshots have complete
current geometry/native identity;105 raw receipts independently replay.
Both stable sampled host points had Y=-1, outside every current Friend1 BOX by 10.028 units in Y. All35 logged new-load lease points also reject those same load7 regions without edge uncertainty;38 native hashes stay empty with no spawn/match lines. Friend1's current cache is entirely zero, while controller currentCount/initialCount still5/5: those fields do not count emitted actors. The survivors retain the original HP17 pack/progress, with zero observed death history. Four saves unchanged, original owned processes exited, rig idle.
This closes missing failing-load geometry/census coverage, not actual predicate return or host-drift causation. A bounded default-off pass-through BOX observer is the next diagnostic candidate, retaining exact consumed input/native return and a completed tick even when no spawn wrapper executes. Controlled native host movement can establish outside-region setup while retaining the original pack before a fresh reload; it remains separately labelled mechanism diagnosis. No host freeze, stale-point replay, native spawning/count/cache writes or gate relaxation establishes recovery. Full ten-cycle and broader gates remain open.
The separately frozen BOX observer now has independent source approval and
Release/Windows ASan controls: each configuration passed167 spawntrace and185
nativehit assertions, with successful DLL builds and no recorded sanitizer
finding. Exact four-source deltas, six binary hashes, final control logs and
byte gates are in build/rig/spawn-geometry-controls-20261003/handoff.md.
The first live attempt reports verified MinHook installation on all three peers;
its selected fresh-load predicate receipts remain unexecuted (setup failed below).
Default OFF; arming requires client load4/transition3 and fulltuple
[5,6,0,1,1,0], after a prior lower lifecycle, with64 selected attempts/2000ms
maximum. Zero predicate calls remain unclassified; all-seven rejection requires
seven ordered current region calls with genuine AL0, complete coverage and no
unexplained loss. Rejection of one particular point by all seven additionally
requires identical16-byte input across those calls. Original calls and the final
lease-copy boundary are preserved.
net_reconnect_shadows_outside_trace.json is the offline validated/root reviewed
282-step derivative: all260 diagnostic objects/full ten cycles remain exact.
One250ms measured native input probe and one100..2000ms continuation must
qualify the original fiveHP17/current outside geometry/identity before fault.
It has no movement retry or direct transform. Run 20261003-191457 failed setup
at step105 after194.3s: all105 preceding steps passed, but the final host position
readback changed during capture. Original five HP17/max20, progress, identity
and zero-death checks passed; pause106 and every reconnect cycle were unreached.
The original strict fixture and FAIL remain unchanged. All326 geometry summaries
remain Waiting/ticks0; there are no selected tick/region/input/native AL receipts.
Installed hooks and zero unarmed ticks do not establish zero-call rejection or
exercise the64-tick/deadline/nesting/unwind bounds. All four protected saves
were unchanged; all eight owned run processes exited and the rig is idle.
The198-file setup-failure archive retains the entire74-file run, full three-peer
logs,103 input fingerprints (101 external source snapshots), three reviews and
an actual-data chart: build/rig/linear-evidence-20261003/kh2-outside-trace-setup-failure-20261003.zip.
net_reconnect_shadows_outside_endpoints_trace.json is a separately named,
282-step derivative. Only the two added outside-geometry assertions
95/105 change; every other280 step and all260 original diagnostic objects remain
exact. Original population/HP/progress/death/identity and full ten-cycle gates
stay mandatory. Its additive sampledEndpoints results evaluate both retained
16-byte before/after readbacks against the current rooted regions, without new
native reads or writes. It requires two snapshots, all three geometry peers and
seven distinct BOX results per endpoint. Original false position-stability flags
remain visible. Outside endpoints do not prove continuous residence, atomic
sampling, actual native AL or movement cause, and cannot retroactively pass the
old fixture. Independent review accepted the exact helper/fixture;15 geometry
and26 adapter tests pass, saved replay preserves all72 legacy rows, and29
fixture mutations are rejected. Source/control/review evidence is frozen in
build/rig/linear-evidence-20261003/kh2-sampled-endpoints-offline-20261003.zip.
Its first sealed native attempt is recorded below. A shorter prefix cannot
establish ten-cycle acceptance.
The separate35-file offline archive is
build/rig/linear-evidence-20261003/kh2-bounded-geometry-observer-offline-20261003.zip.
It is distinct from the349-file diagnostic FAIL archive and its67 pre-run inputs.
Selected native endpoint attempt: first-rejoin population FAIL (2026-10-03)
After James released the desktop, the119-input seal and verified Session1
bridge launched exactly one attempt, 20261003-212208. It failed unchanged
population/hash gate111 in246.3s after setup and genuine same-process rejoin.
Original Friend1 game528568/runtime388948 recovered connection2 to4,
generation2 to5, load3 to4/transition2 to3. Host531376 and Friend2497064 did
not reload. Two complete post-rejoin censuses show[5,0,5]; Friend1's39 fresh
hash headers remain empty, while survivors retain originalIDs1-5/object302,
HP17/max20 and progress9AC5499B. No complete cycle, final native/final capture
or cycles02-10 were reached. The strict earlier setup failure remains FAIL.
Friend1's actual BOX hook now supplies64 selected receipts. Its tick field
is a controller-update sequence, not elapsed milliseconds; per-tick wall-clock
times are not serialized.
Ordinals1-11 were held/incomplete (originalReturned=0, calls0); they are not
complete zero-call native ticks. Ordinals12-64 are complete original-returned
ticks with7 distinct current rooted region calls each. All371 genuine results
are AL0 with identical input bytes BD6AD842000080BF6641B7430000803F, decoded
float4(108.2084732055664,-1,366.51092529296875,1). The captured window directly
proves native rejection of all seven regions at that supplied point; it is not
a continuous host trajectory or an emitter/readiness decision. TickLimit3
stopped at64, with zero dropped/unwound/foreign/overflow/nesting counters.
Deadline, overflow and unwind behavior were not triggered in this run.
All64 full44-byte headers,320-byte record arrays and448-byte descriptors agree
without normalization. Target record-array SHA256
447E6D0F4C6E5AD652F96955D9D3DB86373B7675E112FAC39303429DDFD660DF
matches this run's setup provenance arrays. The observer retains only the
controller's64-byte prefix; this is not a full0x58 controller snapshot or a
continuous incarnation/pending-actor proof. Root independently replayed raw
native fields and corrected its own initial88-byte controller expectation to
the frozen observer's0x40 declaration. Unverified numeric delay metadata from
root replay01 and the timing-unit metadata from replay02 are withdrawn by
replay03; full header bytes remain authoritative.
Three independent reviews cover native geometry, exact population/progress, and lifecycle/collection. All94 terminal run files, three full native logs and 119 sealed input copies are frozen. Automatic report1 is partial because two inject tails changed during collection; its three earlier PNGs are complete transfer/structural evidence. The post-rejoin empty-population trigger was cadence-suppressed, so no second bundle or final post-rejoin screenshot is claimed. Root's fresh audit confirms all eight owned processes absent, empty rig/lock/index, same HEAD and119 inputs unchanged. All four protected save hashes match the seal's before hashes. No retry, movement tuning, gameplay repair, cache/count/flag patch or production source change occurred in the run.
Evidence: build/rig/reconnect-outside-endpoints-package-20261003-01/,
build/rig/reconnect-outside-endpoints-root-replay-20261003-03.json,
build/rig/reconnect-outside-endpoints-native-terminal-safety-20261003-01.json,
build/rig/reconnect-outside-endpoints-native-save-audit-20261003-01.json and
build/rig/reconnect_outside_endpoints_native_20261003_02.png.
At this checkpoint the proposed next step was snapshot-authorized enrollment
with exact native records, bounded partial outcomes and ordered post-cut deaths.
The current policy above instead restricts recovery to all-alive packs and the
recorded host input; death reconstruction and added-dispatch ownership remain
separate, parked work. These AL observations alone close no recovery or
ten-cycle/five-room gate.
Offline native event-gate observer (2026-10-03)
KH2COOP_SPAWN_ENROLL_OBSERVE=1 adds a separate, default-off diagnostic
for the genuine no-argument event gate at3ABC80, matched to expected return
address3FF0E5. It requires the existing trace and valid geometry selection,
and shares that selection's64-update/2,000ms budget. The detour calls the
original exactly once and preserves its AL. The protected final lease-copy
boundary is unchanged. This is a callwise witness, not original-phase proof
or enrollment authority; a complete AL0 receipt alone cannot authorize creation.
Requested-but-unavailable, repeated, nested, unwind and dropped receipts remain
explicitly incomplete. The log's expectedCallerRva is a configured filter,
and updateSequence is not elapsed milliseconds.
Release and Windows ASan builds passed. Each configuration passed198 existing
spawntrace controls, extended for the observer, and185 native-hit controls.
A signed/unsigned test comparison initially failed compilation; its one-line
correction and the original failure logs are retained. Independent review also
corrected a misleading observed-caller label in unavailable receipts; both
native-hit executables were rebuilt and rerun after that serializer change.
Evidence is in build/rig/enrollment-gate-observer-controls-20261003/ and
build/rig/enrollment-gate-observer-independent-review-20261003.md.
No native hook was installed by these controls. At that checkpoint native installation, gate values and live overhead were untested. The subsequent separate native attempt below supplies installation and selected AL0 evidence; negative/unwind cases and live overhead remain unmeasured. This candidate is separate from the earlier sealed endpoint run and creates no actors or recovery acceptance evidence. For the parked added-dispatch alternative, pending occupancy, controller incarnation and a serialized check-to-dispatch window remain unresolved. The current scheduled-update policy uses its explicit prestate/recorded-input qualification and does not inherit an added-call B1 requirement.
Native event-gate attempt: first-rejoin population FAIL (2026-10-03)
The separate130-input attempt 20261003-221650 used the unchanged282-step
endpoint fixture and enabled the reviewed event-gate observer only in its child
environment. Runner457128 executed in Session1 under the existing desktop
bridge; all three live OS-associated injected copies matched the sealed Release
DLL. This is loaded-module association plus on-disk hash, not an in-memory
code digest. All three reported independent gate installation/coverage1.
The run failed the same population/hash gate111 after243.7s, with111 preceding steps passing and zero complete cycles. Friend1 timed out/rejoined/reloaded in its original process and stayed physically empty; survivors retained the original five object302 enemies at17/max20. Its selected load4/transition3 capture contains64 receipts: nine held/no-call/nonreturned captures remain incomplete, and55 complete single-call gate receipts retain actual AL0. Each complete receipt joins seven distinct current BOX calls, all385 native returns AL0. Each tick uses one exact point across its seven calls; seven point values occur across the window. This is captured-input evidence, not continuous trajectory, original-phase authority, pending exclusion or a creation fix.
TickLimit64 was exercised. The traced63224..63854 span is630 controller updates, not milliseconds. All selected ordinals and joins are present, with no selected nesting/unwind/overflow; periodic dropped/unwind counters stay0. Global event-gate foreign counters include ordinary unselected calls and do not mean selected corruption. Native AL1, unwind, overflow and deadline cases were not exercised, and timing overhead was not benchmarked. Record320 bytes retain the same exact current-pack digest; the controller receipt remains its 64-byte prefix rather than the full0x58 allocation.
Root's terminal audit confirmed all eight owned processes absent, empty rig/
lock/index, sameHEAD and all130 sealed inputs unchanged during the attempt.
All four protected save hashes match before. The94 run files, three full
native logs, inputs, loaded-module receipt and independent reviews are retained
in build/rig/event-gate-observer-native-package-20261003-01/; root raw replay
is build/rig/event-gate-observer-root-replay-20261003-01.json. A fresh manifest
arrival does not prove living native population, and a progress receipt's byte
length is distinct from bytes actually changed.
Automatic report1 is complete for the original roster: all12 peer artifacts have checked integrity, its native log tails are bounded/truncated rather than interrupted, and its three PNGs precede the pause. Aggregate collection remains partial because report2 records an active-collection witness and report3 is cadence-suppressed after rejoin. Neither is a final recovery screenshot or an independent collected bundle. This differs from the earlier attempt's interrupted tails and must not inherit that earlier artifact status.
Scheduling RE now resolves the native game-thread startup and synchronous cooperative task dispatch. The same-thread fiber model makes TLS/thread ownership insufficient for interval exclusion. Exact allocator/resource callbacks, pre-link creator affinity and full mutation coverage remain open; no borrowed lock or safe check-to-dispatch boundary is approved. The next observational work collects active/deferred occupancy and raw record IDs while keeping pendingExclusionComplete=false. All original ten-cycle/five-room/ natural-personal/remote population and death acceptance stays open.
Read-only raw active/deferred occupancy receipt (2026-10-03)
native_enemy_census now adds a separate rawOccupancy receipt after its
existing census and geometry reads. It walks both active and deferred roots,
retains every node before type/status/mask filtering, decodes native handles,
and reads the u16 record ID at each readable nonnull record pointer. Null
records, unreadable IDs and observed ID zero remain distinct. Checked roots,
bucket bases, links and metadata are read again; changes, missing reads,
cycles, cross-list membership, tail mismatches and the combined256-node cap
leave explicit partial evidence. Work shares the original per-peer deadline.
The CLI keys peek values by address only. Different types at one address would
overwrite one another during JSON decoding, so the raw collector rejects
such conflicts across its accumulated field set before issuing the affected
read. Same-address/same-type labels remain supported. Independent review first
held this defect, then accepted the scoped correction; both reports are
retained. Root executed all27 geometry/raw-occupancy controls successfully,
including byte-backed collision cases and unchanged legacy output/read order.
Evidence: build/rig/raw-occupancy-controls-20261003-01/ and
build/rig/raw-occupancy-independent-review-fixed-20261003.md.
At that offline checkpoint this source was newer than the frozen
20261003-221650 inputs and unexecuted. Those older saved snapshots lack
deferred roots and cannot supply complete new occupancy receipts. The separate
native attempt below uses the new reader. A checked sample is not atomic and cannot
exclude ABA, foreign creation or pre-link allocations. pendingExclusionComplete,
controllerIncarnationQualified and globalControllerIdCoverageComplete
remain false. No population/readiness/hash result is promoted and no actor
creation is enabled.
Dedicated raw lifecycle bookends: schema 2, offline only (2026-10-03)
The later schema2 source brackets the raw interval with its own checked native headers and completed-load/arrival logs. Both samples retain load and transition serials, epoch, the full six-field location, gameplay state and handle regions. Each header's active head/tail and all64 regions must also agree with that phase's raw roots/buckets. Missing, unsafe, changed or contradictory samples keep the observations and prevent completeness. The two added79-field header peeks share the original census deadline; there is no retry, game write or change to the earlier measurement schedule.
Initial32 controls passed, but independent review reproduced stable empty
header roots against a stable two-node raw list, and contradictory bucket0,
both incorrectly marked complete. The correction adds explicit per-phase
joins and missing/mismatch reasons. Root's34 focused controls and an
independent34-test run pass; replay of both original defects now retains the
nodes and marks the receipt partial. Unused bucket63, either/both phase
conflicts, missing fields, lifecycle drift and deadline exhaustion are covered.
The initial HOLD and corrected PASS evidence remain separate:
build/rig/raw-occupancy-lifecycle-v2-controls-20261003-01/,
build/rig/raw-occupancy-lifecycle-v2-controls-20261003-02/, and
build/rig/raw-occupancy-lifecycle-v2-independent-review-fixed-20261003-01.md.
This is post-run source, not an executed schema2 capture. The frozen native attempt below remains schema1 and FAIL. Matching endpoint observations are non-atomic and do not exclude ABA, pending creation, incarnation reuse or global record-ID aliases. All authority flags remain false.
Native raw occupancy: selected records absent from checked lists (2026-10-03)
The140-input attempt 20261003-230406 executed the reviewed reader with the
unchanged282-step fixture. It failed the original population/hash gate111
after246.7s, zero complete cycles. Friend1's original game/runtime rejoined
and completed one fresh native load; both post-load censuses remained5/0/5,
with the survivors retaining original IDs1-5/object302/HP17/max20 and progress.
The six census files retain36 raw schema1 receipts and500 node observations. Seventeen receipts have equal checked fields;19 remain partial for34 flags120 changes. All644 raw read-work calls completed. All108 record-ID observations on status-null nodes survive classification; null records remain distinct from ID zero or missing reads. Deferred roots are explicitly zero in every sample; nonempty deferred traversal was not exercised natively.
Both Friend1 after-rejoin raw samples are complete:10 active nodes, zero deferred nodes, six record IDs3,5,4,9,10,171 and four null record pointers. Neither sampled list matches the adjacent selected controller, record span or IDs11,12,13,14,18. This narrows hidden listed-actor alternatives at those samples. Raw collection follows the older census/cause lifecycle bookends and does not independently bracket its entire interval with load/transition/tuple reads. These are contextual checked lists, not a new lifecycle or exclusion token. The subsequent schema2 source has passed offline controls and review with dedicated raw lifecycle bookends; it was not used by this attempt.
The new native trace separately has56 complete single returned gateAL0 captures joined to392 BOXAL0 returns, plus eight no-call/nonreturned captures that remain incomplete. Three actual point values occur across the complete window. The65324..65954 span is630 controller updates, not milliseconds; this remains callwise evidence rather than original-phase or creation authority.
Independent raw, gate and population/lifecycle reviews accepted the frozen
evidence. The94 run files,140 inputs and three native logs are preserved in
build/rig/raw-occupancy-native-package-20261003-01/. Root's04:11:37UTC audit
found all eight owned processes absent, empty rig/lock/index, unchangedHEAD,
all140 inputs unchanged during the run and all four save hashes unchanged.
An OS module query happened after cleanup, so this attempt has no captured
loaded-module path/hash association; the earlier run's proof is not reused.
First automatic report and aggregate collection are partial:12 received peer artifacts have checked integrity, but Host's bounded native tail is interrupted by source-log changes. The three fresh original-roster PNG receipts precede the pause; there are no final post-rejoin views. Active1/cadence2 witnesses are suppressed triggers, not additional collected bundles.
Static callback review maps the allocator and model virtual targets, including
a real allocator semaphore that releases before construction. Object-entry
type4, BAR type4 and model discriminator3 are distinct; no current target
binding or full creator/fiber/pending closure follows. See
build/rig/surviving-enrollment-resource-callback-closure-20261003.md.
Native resource/allocator binding, controller incarnation, global-ID coverage,
serialized check-to-dispatch and fresh full-set/partial/death semantics remain
prerequisites. All original recovery and combat acceptance gates remain open.
Allocator/model pointer-binding helper: offline review (2026-10-04)
The separate capture_native_resource_bindings helper has passed14 focused
controls and19 independent adversarial cases. It samples the allocator root,
recognized vtable/slot, and qualified object302 actors' first BAR type4 entry,
fresh handle bucket, signed model kind and exact SKL/BG/Multi callback slot.
It retains typed little-endian bytes and rechecks the original addresses,
local lifecycle and parent raw scope. Mixed-type aliases, missing reads,
unsupported shapes, drift and the16-actor/64-BAR caps keep partial evidence.
All reads use the supplied census deadline; no retry or unknown virtual call
occurs. A sampled pointer/slot does not prove an earlier executed callback.
At acceptance the helper had zero production calls, with every existing
production AST unchanged after removing it. Exact source/test snapshots and
root controls are in build/rig/native-resource-binding-controls-20261004-01/;
the independent report is
build/rig/native-resource-binding-independent-review-20261003-01.md.
The subsequent default-off integration passes18 focused controls and a
separate independent review. native_enemy_census accepts the strict boolean
resourceBindings; true appends the receipt after raw occupancy using the
same remaining per-peer deadline. Omitted/false preserves the original reader
schedule, and a binding-only failure retains partial evidence without changing
the parent verdict. The separate282-step
net_reconnect_shadows_resource_bindings_trace.json fixture enables only its
first six census steps (indices42,64,76,93,104,108); every other fixture field
matches the original. See
build/rig/native-resource-binding-integration-independent-review-20261004-01.md.
No native binding capture, callback closure, incarnation, pending exclusion or
creation authority has been established.
A separate desktop wrapper candidate preserves the original runner while
polling fresh rig-owned PID/FILETIME identities for loaded-module path and
disk-copy hash observations. Its PowerShell syntax and embedded C# compilation
pass independent review. A subsequent derivative's read-only Windows API probe
passed against its own PowerShell process; game ownership/ancestry/module
observation and gameplay overhead remain unexecuted. The explicit
future-seal placeholder blocks launch. Missed launcher ancestry, errors or
identity drift keep association unavailable; disk hash is not an in-memory
code digest. See build/rig/binding-module-wrapper-independent-review-20261004-01.md
and build/rig/resource-bindings-wrapper-review-20261004-01.md.
Native resource-binding/lifecycle diagnostic (2026-10-04)
The separate 20261004-003916_net_reconnect_shadows_resource_bindings_trace_1
run executed the accepted integration and schema2 raw lifecycle reader.
It remains FAIL at step111 /255.0s, with111 preceding passes and zero
complete cycles. Friend1 kept its original game/runtime, retired connection2,
rejoined as4 and completed load4/transition3. Both post-rejoin native censuses
still report5/0/5; the surviving five object302 actors retain HP17/max20.
Full progress8108bytes precedes load; equal personal hash124AA108 is checked
at its application boundary, not throughout the natural reload.
All36 dedicated raw lifecycle samples and72 active-root/all64-bucket joins are stable/complete. Raw occupancy remains13 complete/23 partial from33 flags120 changes; lifecycle stability does not erase those partials. The500 retained node observations include two complete Friend1 post-load lists with ten active nodes, empty deferred roots and IDs3/5/4/9/10/171. Its own64 current definition blocks identify selected Shadow IDs11/12/13/14/18; neither raw sample matches the selected controller, exact record pointers or ID/object tuples. No later refill, pre-link exclusion or incarnation is established.
The36 resource receipts retain allocator root/vtable/slot bookends;13 qualify complete and23 retain incomplete parent raw scope. Four receipts attempt three actors each: ten complete ModelSKL observations and two partial from additional flags120 drift. All observed graphs select first BAR type4, signed model kind3, vtable M+5B3E00 and slot M+1C21E0. No nonempty actor set has complete binding coverage; eight coverage-complete receipts explicitly have zero actors. Friend1 after rejoin supplies allocator evidence only. Sampled targets do not prove callback execution or safe creation; all authority flags stay false.
The wrapper records current owned PID/FILETIME/launcher/module observations
and matching staged-DLL disk hashes for all three peers, independently reviewed
against the exact final wrapper/launch/ownership receipts. Final native
Creation/Alive and second-lock results are attested by that wrapper's success
branch rather than separately serialized raw calls. This run has54 complete
returned gateAL0/seven-BOX-AL0 captures (378 BOX returns) and ten held/no-call
ticks; held default zeros are not returns. In-memory code digests, original
phase and eligibility remain unavailable. See
build/rig/resource-bindings-native-module-gate-review-20261004-01.md.
First and aggregate automatic collection are partial: all12 peer byte hashes
and relay hash verify, but Friend2's bounded native tail is interrupted and
one cadence witness is suppressed. All three1920x1080 PNGs precede the pause,
with the original roster; they are not final post-rejoin views.
Root's05:43:40UTC terminal audit found all eight owned processes absent,
rig/lock/index empty, unchanged HEAD,165 inputs unchanged during the run and
all four protected save hashes unchanged. The94 run files, three whole native
logs and exact inputs are frozen in
build/rig/resource-bindings-native-package-20261004-01/. Independent binding
and lifecycle audits are resource-bindings-native-binding-audit-20261004-01.md
and resource-bindings-native-lifecycle-audit-20261004-01.md under build/rig/.
Earlier native packages keep their original schemas and verdicts. All recovery,
combat, ten-cycle/five-room/natural-personal/physical-remote gates remain open.
Full records/original-phase native attempt: first-rejoin FAIL (2026-10-04)
20261004-020622_net_reconnect_shadows_controller_records_phase_trace_1
executes both new observations on the unchanged 282-step parent. It fails at
step 111 after 267.1s: Friend1 retires connection 2 and rejoins as 4, with local
load/transition 3/2 to 4/3, but both post-rejoin censuses remain 5/0/5. Zero cycles
complete; steps 112-281 do not execute. Source is uncommitted and creation stays
disabled. Successful diagnostic collection does not satisfy population recovery.
All 36 samples freshly retain ten supported ordinary definitions and all 26 full 64-byte records, with matching local before/after 44-byte headers and record bytes. The 936 repeated record observations cover 26 distinct record contents; they are not 936 actors. Complete ordered record arrays are byte-identical across all peers/checkpoints. Header 30 alone changes its activation byte +0xE across checkpoints, so full-header hashing is not an immutable identity. The four same-group type 2 headers remain distinct 30/31/32/33; group alone is ambiguous.
Combined table/list/lifecycle coverage is 8/36: six initial empty observations and both empty Friend1 post-rejoin observations. This closes the prior 21-full- record/15-ID gap in those sampled ordinary scopes. The other 28 joins remain partial for flags120 drift and parent comparisons; raw-v2 itself is 16 complete/ 20 partial with 35 flags changes. Each failing Friend1 active list has ten nodes, six nonnull references with IDs 3/4/5/9/10/171, four null records and zero deferred nodes; none matches the five selected Shadow records 11/12/13/14/18. Those five records nevertheless exist in the full definition array. No sampled raw-u16 ID collision, zero/high-bit ID or nonempty array alias was observed. This is neither an executed native lookup nor global/incarnation/pending/atomic/creation proof.
All three peers report the new phase observer configured. Only Friend1 emits selected load 4/transition 3 rows: 64 sequences 44-107, update counters 73155-73785. Fifty-five returned calls match the serialized original-update thread bracket, source 7 and actual update/gate caller RVAs 3A5063/3FF0E5, with 55 gate AL0 and 385 BOX AL0 results. Nine first-lease holds have zero calls/returns and unavailable default boundaries. Every BOX input repeats one point (-166.228271484375,-1, 403.697265625,1); this is not a varying-point eligibility scan. Decision-time configuration/coverage atomics remain unrecorded; TLS cannot prove fiber continuity. All fiber/eligibility/incarnation/pending/creation authority remains false. Ninety-seven generic trace rows with multiword reasons remain tokenizer findings outside selected joins; no malformed selected join was discarded.
Current owned PID/FILETIME/launcher-to-this-runner joins qualify all three loaded DLL paths and staged-copy disk hashes, with no in-memory digest. Automatic collection remains partial for both reports 1/3; active suppression 1 is retained. The two reports' 24 artifacts include one interrupted tail each. Six fresh 1920x1080 PNGs are retained as checkpoint images, with no final-state claim. The terminal desktop audit records all 218 inputs unchanged during the attempt, four protected saves unchanged, all eight owned processes absent, empty rig/ index and unchanged HEAD. Subsequent root documentation edits do not rewrite that during-run claim or the frozen input copies.
Frozen package: build/rig/controller-records-phase-native-package-20261004-01/.
Record byte/association audit: controller-records-native-id-audit-20261004-01.md;
phase raw audit: controller-records-phase-native-phase-audit-20261004-01/;
independent native review: controller-records-phase-native-independent-review-20261004-01.md.
All original ten-cycle/five-room/natural-personal/remote gates remain open.
The bounded branch deduction and independent review confirm that every genuine
BOX AL0 takes 3FF142 -> 3FF105, bypassing the ordinary function's sole
3FE320 call. The seventh node's zero next handle resolves to null and ends
that loop. This is conditional on the checked caller, saved code and sampled
list binding, with no continuous ABA/fiber or whole-program exclusion claim.
The current host point therefore cannot recreate this pack through that ordinary
path. Internal first-failing comparison PCs remain saved-code/operand deductions,
not instrumented execution: six boxes reject on X before Y, one on upper Y.
No geometry, flag or coordinate override follows. Evidence and review are
build/rig/native-ordinary-box-negative-path-deduction-20261004-01.md and
build/rig/native-ordinary-box-negative-path-independent-review-20261004-01.md.
The opaque execution-context marker design passed independent review but stays unimplemented; no earlier sample is retroactively qualified. At that checkpoint portable exact record-content comparison and a bounded native catalog reader were the next components; their later implementation/results are documented above. Content correspondence alone remains distinct from lifetime/pending facts. The stricter added-call exclusion and branch-installation route is parked.
Offline original-update bracket observer (2026-10-04)
KH2COOP_SPAWN_ORIGINAL_PHASE_OBSERVE=1 enables an additive diagnostic only
when the existing trace, geometry and event-gate observers are configured and
installed. It adds no native hook. All seven original update call sites retain
their arguments and order; explicit source/hold tags, actual hook callers and
POD invocation tokens distinguish lease work, original entry, normal return and
unwind. [spawnenrollment] original-phase and its gate boundary rows share the
existing selected event sequence. Unavailable boundary rows and held default
AL0 bytes are not executed/returned calls. Existing gate/BOX completeness and
production decisions remain unchanged.
The actual production-boundary harness passed232 controls in Release and232
in Windows MSVC ASan; independent review verified exact candidate hashes,
seven compiled original calls, unchanged final lease-to-call arguments with no
added calls, SEH restoration, and the serializer's legacy projection. The
two-fiber control demonstrates that threadPhaseConsistent can be true across
an unrelated fiber call: fiberContinuityProven and originalPhaseEligibility
remain false. Positive caller tests use controlled expected-RVA seams; no native
caller execution is claimed. The production DLL builds and links. The subsequent020622 attempt above
records the new rows in game; broader timing/stability remains untested.
The disabled hook dispatch retains its old source path, while RunUpdate gains
SEH/frame/TLS overhead even with the option off. Finalization-time configuration
and coverage atomics are checked by the submitted source but are not individually
serialized; a later raw-row audit cannot fully replay those decision-time values.
None of this establishes eligibility, controller incarnation, pending exclusion
or creation authority. Candidate/checks are frozen under
build/rig/native-original-phase-implementation-20261004-01/; independent review
is build/rig/native-original-phase-independent-review-20261004-01.md.
The preceding003916 native package and verdict remain unchanged.
Offline full controller-record inventory (2026-10-04)
A native_enemy_census step can set the strictly Boolean, default-false
controllerIdCoverage option. When enabled, collection follows the existing
raw schema2 and optional resource-binding diagnostics under their same remaining
deadline, with no retry or new timeout. Disabled steps add no native reads.
The separate net_reconnect_shadows_controller_records_phase_trace.json fixture
adds this option only at six census steps (42,64,76,93,104,108); all282 original
steps and acceptance conditions remain intact. The launch wrapper separately
enables the original-update phase observer described above.
The reader freshly inventories every inline ordinary-controller entry and supported definition's full44-byte header and declared64-byte records. It retains script/unknown types, ID0/high-bit IDs, duplicates, aliases, failed words, omitted ranges and before/after changes. Table byte coverage, active/deferred reference joins and combined sampled coverage are separate results. Both raw lists, all64 buckets, local lifecycle phases and parent/cause projections must agree before combined coverage can be complete. A partial parent does not erase successful table bytes; no-conflict stays unknown without complete sampled coverage unless an actual duplicate/alias is already observed.
Limits are64 table entries,256 raw nodes,256 records per definition and1024
logical record slots. Reads use the existing checked128-field adapter. Twenty-one
focused controls passed on the final source; independent review added six
adversaries and verified default-off behavior and exact fixture projection.
The unchanged geometry/resource closures reuse their34/18 prior regression
results with the source-hash qualification in the producer receipt. The subsequent020622 attempt above records complete native table/record
collection, partial stronger joins and measured durations; broader timing
behavior remains untested. Pointer agreement cannot establish atomicity or incarnation; all
globalControllerIdCoverageComplete, controllerIncarnationQualified,
pendingExclusionComplete, atomic, creationAuthority and
executedCallObserved flags stay false. This is diagnostic collection, with no
population/readiness decision or creation change. Receipts are
build/rig/native-controller-id-coverage-root-acceptance-20261004-01.json and
build/rig/native-controller-id-coverage-implementation-independent-review-20261004-01.md.
Current secondary equipment/map bindings (2026-10-04)
native_enemy_census accepts the strictly Boolean, default-false
secondaryBindings option. It captures the three bounded object tables, first
matching went/item BAR descriptors and relocated roots, current slot0/1
equipment, ordered item first-match and selected raw mapping cells. Every new
peek checks PID/module identity and uses the census's remaining30-second
deadline and existing128-field batches. Omitted/false preserves the reader
schedule. Caps, malformed counts, missing reads and drift retain partial data;
original disk formats are not substituted for current native layouts.
Consumed handle buckets must agree with both final readback and the independent lifecycle endpoints. A known parent-scope conflict denies combined stability; an unavailable parent remains explicitly local-only. The initial independent HOLD exposed both omissions. The fixed source passed128 affected controls and the exact original adversarial replays in a separate independent review: fixed review. All creation/exclusion/lifetime authority flags remain false. These sampled current selections do not qualify native object bsearch, future equipment or arbitrary replacement arguments.
net_secondary_bindings_current_shadows.json copies the canonical courtyard
fixture's first12 setup steps, then makes one opt-in three-peer census and
requires complete stable bindings joined to its parent scope. Its table
collector is read-only; the existing boot/warp/runtime and player-protection
steps retain their normal behavior. The14-step fixture passed in153.4s:
20261004-112324.
Independent saved-byte replay verifies all297 secondary batches and their
PID/module identities. Each peer has1900 primary object rows, two observed null
roots,81 nonzero-group rows,87 selected records and174 current slot results.
There are91 nonzero sampled outputs and83 explicit early-zero outcomes per
peer; no sampled raw output is302. Live went is3828 bytes, live item is15272
bytes with535 ordered records. This qualifies current selected cells, not an
all957-word went scan or future replacement-argument domain.
The complete parent censuses contain zero living enemies on all peers. The run ended before activation and supplies no five-Shadow presence or recovery acceptance. All160 sealed inputs and four protected saves match; the runner exited0 and released its owned instances and lock. No OS-loaded DLL association or in-memory code digest was captured. See the independent native review for exact scope, branch distinctions and preserved authority limits.
Offline surviving-population assessment (2026-10-03)
This assessment was completed during James's Rivals desktop hold, before the endpoint attempt above. No gameplay, live census or process operation was part of this saved-data assessment. The later run used a fresh preflight/input seal; it does not retroactively extend these historical observations.
The frozen setup logs contain15 fixed-record, nonnull native wrapper returns
with complete AllocationPassed factory receipts. All15 immediate actor reads
were unavailable; two later complete native censuses per actor supply30 current
controller/record/binding correlations. ReadTraceActor can short-circuit before
reading controller, record, type or HP, so logged default zeros are unavailable
fields, not proof of native zero, death or failed allocation. The audit's
readyAtWrapperReturn=0 counts qualified observations; it does not prove every
actor was actually unready. Later same-address matches do not establish
allocation continuity across the gap. Original one-shot trace events remain
unchanged; no delayed readiness confirmation has been implemented.
A fresh complete living set could authorize a new snapshot-defined enrollment
instead of replaying historical creation events. It still needs an exact unique
portable native-record map and current controller incarnations, complete local
record/cache/occupancy inputs and exclusion of pending actors, aliases and
unsupported history. Current manifest spawnIndex is observation order and
must not be treated as native record index.
Follow-up actual-data comparison establishes all thirty full64-byte record
receipts across the six setup peer observations are identical without masking
or normalization: the five-record320-byte array hashes to
447e6d0f4c6e5ad652f96955d9d3db86373b7675e112fac39303429ddfd660df.
Actual actor/provenance/log joins map IDs1-5 to native IDs11,12,13,14,18.
Full raw headers/controller bytes are unavailable, including initial-delay
header+1C. Equal local pointer values give no relocation-diversity test, and
sequential samples give no atomic capture/lifetime proof. The empty failing
load7 still lacks its own full record array. See the static-record-map report
and its actual-data receipt/producer in build/rig/.
Saved-PE review proves the exact type2 branch of3FE320 ignores the region argument, activates the header and calls the full3FE6F0 native emitter. AL1 is only the branch result. Actual five-record delay8 causes a positive cooldown after the first successful emission; dispatcher-only repeats cannot complete the pack. Retaining the original3FF000 update may supply native cooldown progression over bounded ticks, but direct dispatch still omits outer event/ disabled gates and accepted-region bit3. A normal update return is not proof those gates passed. No flag/count/cache patch or predicate override is approved as a substitute for that proof.
The exact saved3ABC80 event predicate is now independently resolved: it is read-only, takes no arguments and returns Boolean AL. Nonzero suppresses work; only zero reaches cooldown/region testing. Its false condition is native flags 2A11400&0x102==0, qword2A11478==0 and dword2A10500==0. This static contract does not establish those values or actual gate passage on a target native tick.
Accepted-region bit3 is transient geometry state, set before dispatch rather
than as proof of creation. Confirmed consumers include NPC inactivity/removal
logic and a script Boolean query. Raw RTTI and handler bindings distinguish that
NPC path from the current Shadow/ZAKO handler; no Shadow-loss cause is proved.
An outside-region living-set contract may intentionally retain bit3clear while
preserving truthful geometry. Its supported actor/script/lifetime behavior
needs proof; setting bit3 to imitate an accepted path is not justified. See the
event-gate-contract and accepted-flag-consumers reports in build/rig/.
The lead accepted this as a stronger candidate for a scoped proof, with fresh enrollment/reinitialization and pending-actor witnesses, actual eligibility/flag semantics, bounded partial outcomes, post-cut authority/death handling and later native readiness/death bookkeeping still unresolved. Partial creation is not rollback and must not trigger culling, resets or replacement retries. Original five IDs/HP17/max20, complete no-extra population, shared progress, zero-death, full ten-cycle, five-room, natural/personal and protected-save gates remain. Empty rooms retain their separate complete-native-empty readiness contract.
Evidence: build/rig/surviving-bootstrap-return-readiness-audit-20261003.json,
the authority, native-enrollment, acceptance-controls and full-batch-review
reports in build/rig/, and the frozen saved PE. This assessment changes no
C++/DLL/helper/fixture and establishes no production fix or native acceptance.
Same-room absolute HP ordering (2026-10-03)
Protocol 7 supplies a nonzero 64-bit sequence from the existing checked DLL HP publication point. Relay and client admission reject older same-room values; the client checks before raw bridge forwarding, preserving reliability through conditioning. The DLL also keeps an independent floor. Reliable equal-sequence cache replay remains supported. HP_ORDERING.md describes the namespace/reset rules, source exhaustion and remaining full-resync boundary.
The frozen protocol 6 baseline had three reproduced failures: confirmed HP 72
became 41 after an older packet, and targeted/late-join cache replay both retained
41. Its real loopback receipts and source are in
build/rig/hp_order_failed_baseline_receipt_20261003.json and
enemy_hp_order_baseline_20261003. Native and remote acceptance remain open.
The final protocol 7 receipt is build/rig/hp_order_offline_receipt_20261003.json:
Release and Windows ASan passed 1,560 checks each, including 89 actual HP
ordering controls and 118 production native consumer/producer controls over
owned memory. Portable ASan+UBSan passed 1,116; Windows-only controls are excluded.
Nine prepared scenario linkage cases and three actual PID-0 runtime smoke checks
passed, with no sanitizer findings. These establish offline behavior, not native
gameplay or physical multi-PC acceptance.
Automatic all-peer desync reports: offline candidate (2026-10-03)
The then-current protocol 8 / AvatarBridge 2 / WorldBridge 10 candidate
automatically collects bounded logs, metadata and screenshot bytes from the
frozen original roster when the relay emits its persistent-mismatch notice.
Diagnostics use a separate reliable channel and preserve partial/interrupted
results, checked digests, capture attempt witnesses and suppressed-trigger
evidence. Scenario launches register exact inject logs and their own spool
roots; reports link complete and incomplete aggregate artifacts independently
of gameplay PASS. DESYNC_REPORTS.md specifies limits,
paths, versioning and controls. The frozen protocol 6 receipt is
build/rig/desync_offline_receipt_20261003.json. A separate current-protocol
native report from 20261003-135207 supplied intact 1920x1080 renderer captures
from all three original local peers. It correctly remains partial because one
inject log changed during capture. Saved artifact digests, PNG validation and
the bounded review are in resync_shadows_auto_report_review_20261003.md.
A later separate report 1 from 20261003-140717 is complete for all original
three peers with matching bytes/digests, stable bounded logs and successful
native capture witnesses. Its run-level index remains partial for three
suppressed triggers. Final explicit captures have path/PASS records but lack
stored request/done receipts; the earlier automatic witnesses cannot replace
them. See resync_population_captures_review_20261003.md. Physical remote
contributions and full gameplay recovery remain open; all VUH-1508 acceptance
criteria stay open.
Relay world cache epoch validation (2026-10-03)
The frozen protocol 5 / AvatarBridge 2 / WorldBridge 8 slice rejected old, future or zero-epoch holds/manifests/HP/deaths before forwarding or changing the relay cache. Each must match the current nonzero room epoch; HP/death also require that room's manifest epoch. All six cached world types require exact outer framing and complete payload decoding before mutation. Previously, delayed HP and deaths could be replayed under the current manifest's epoch; a stale manifest or hold could replace current cached state.
Valid current-room append, pre-append unknown HP IDs and duplicate HP last-entry-wins semantics remain. An initial HP sample arriving before its current manifest is discarded; subsequent periodic samples supply fresh HP. Room-transition monotonicity and progress-version admission remain unchanged. This patch does not fence old same-epoch HP within a forced-resync transaction.
The frozen original relay reproduced 33 failures in the 54 new controls,
including actual targeted-cache-resend and late-join reconstruction witnesses;
its 148 prior checks passed. The fixed relay passed 202 world, 127 recovery
and 61 bridge checks each in Release and Windows ASan (390 per configuration).
Portable ASan+UBSan passed 202 world and 127 recovery checks (329), with leak
detection enabled. Builds and test executable/log hashes are in
build/rig/world_cache_epoch_offline_receipt_20261003.json; baseline failures
and the initial source-review correction are preserved. The first candidate
missed an outer frame-length check; independent review caught it before runs,
and the final source includes that guard. Both reviewers accepted the fix.
Unreliable HP callback absence alone is not proof that nothing was transmitted;
reliable cache-reconstruction assertions independently prove the cache invariant.
Native forced resync remains open. At this frozen protocol 5 slice, the host request only resent cache: a same-epoch transition is ignored, identical progress does not repair live SAVE, and fresh unbound actors intentionally cannot inherit historical host deaths. Full recovery needs authenticated transaction/delivery fencing in both directions, complete bootstrap, a checked new native load and convergence ACK, and independently verified dead-spawn identity without removing the anti-refill guard. The protocol 8 implementation now provides the transaction boundary (FORCED_RESYNC.md); it still reports dead-bootstrap ambiguity unavailable. A reset/reload or empty-room pass cannot substitute for nonempty recovery.
Automatic reports were unimplemented at this frozen slice; the later protocol 6
candidate is described above. Separate frozen audits are in
build/rig/forced_resync_relay_audit_20261003.md,
forced_resync_native_audit_20261003.md and desync_bundle_audit_20261003.md.
No source/design audit certifies native or remote behavior. All VUH-1508 criteria
remain open. No gameplay, installation, save/protection changes, staging or commits
occurred in this offline slice.
Dropped-friend recovery: offline candidate (2026-10-03)
This frozen offline slice used protocol 5, AvatarBridge 2 and WorldBridge 8. The v5 wire shapes are unchanged from v4; typed closure and opaque world-incarnation semantics require the version gate. Each accepted Player lifetime mints an OS-random 128-bit session token. The configured session name is a label. Friend rejoin pins the original token, host peer/connection and exact local peer/slot; repeated connection counters after relay restart cannot identify the old world. Changed/missing host or incarnation is terminal.
RuntimeMain now uses production ClientRecovery before its attachment wait. There is one outstanding attempt, separate four-second transport and verified roster deadlines, five retries after 1/2/4/8/8-second delays, and a 60-second episode limit. Only an already admitted Friend1/Friend2 retries transient transport/idle/handshake/occupancy/full-lobby loss. Initial failure, Player loss, explicit shutdown, incompatible/rejected identity, host departure and relay stop are terminal. HelloReject classification precedes deadline handling. Ten uninterrupted seconds of admitted membership reset the budget; this is network stability, not native bootstrap readiness. Heartbeat/clock activity continues while unattached or awaiting admission, with authoritative traffic quarantined.
Every local/remote retirement clears runtime world/avatar/mailbox state and advances the mapping generation. The DLL immediately retires cached host HP, deaths, progress and queued warp on a header change, even when the matching FIFO reset is deferred behind a full old ring. Only the current reset marker arms world ingestion. HP/death/progress/warp leaves recheck their captured generation; the first new full progress snapshot survives the ordered reset. A progress retirement during application stops further writes and fails readiness; an already written prefix is not rolled back. These repeated boundaries are not an atomic transaction with the runtime. Outgoing v8 records retain the producer's generation, so queued old claims/ACKs/hashes cannot be relabeled after rejoin.
Focused controls use the actual coordinator and ENet clients/relay. Only Friend1 expires while the host and Friend2 survive; reconnect receives the existing full merged progress, room/hold/manifest/HP/death cache without any post-drop host resend or cached activation. That relay progress fixture is three bytes, proving merge/order rather than native full-SAVE readiness. Separate owned-memory controls include actual EnemySync, ProgressSync and Warp over a complete allowed progress fixture and synthetic native callbacks. A scripted ENet endpoint emits real world/avatar/actor/enemy/event witness packets, with ordered ClockPong barriers and positive callback controls, to test pre-admission and rejection quarantine. Pin mismatch/missing/truncated roster closes once before publication; HelloReject1 remains terminal even when the later raw disconnect code is zero.
Release and Windows ASan each passed 127 recovery, 146 avatar, 148 world, 85 simulation, 61 bridge and 99 production-consumer checks (666 fresh checks per configuration). The unchanged 125 trace and 166 policy controls were reused only after matching their executable and passing-log hashes: 957 checks per Windows configuration in the receipt. Runtime/relay/tool and both DLL builds passed. Portable ASan+UBSan passed 127 recovery, 148 world and 85 simulation controls (360), with leak detection enabled and no sanitizer finding. Windows native SEH modules have ASan evidence and no UBSan proof; AvatarBridge controls are Windows-only and are excluded from the portable subset.
Portable leak detection exposed a 163-byte ENet packet leak during teardown. Relay/client send paths now handle allocation failure and destroy caller-owned packets on failed submission, retaining ENet ownership on success. The original leak log and clean rerun are retained. Avatar send timing, host-expiry priming and the repeated-Hello fixture now establish actual admission before their witness send; all stale-packet, expiry and refusal assertions remain. The evidence wrapper's incorrect assumption about trace/policy summary format was corrected, then completed passes were reused by exact hashes. Failed fixture/build and wrapper logs remain alongside the passing evidence.
Frozen reconnect-slice evidence paths are build/rig/client_rejoin_offline_receipt_20261003.json,
client_rejoin_control_results_20261003.json,
client_rejoin_portable_results_20261003.json and the per-suite/review artifacts.
No candidate is installed live. Native gameplay rejoin, puppet restoration,
full room/program/HP/progress convergence, forced resync, native/remote desync bundles
and remote-network recovery remain open on VUH-1508. Historical v3 native and
v4 offline receipts do not certify this candidate. All acceptance criteria stay
open; no gameplay, save writes, protection changes, staging or commits occurred.
Avatar connection replacement and cached-pose authority (2026-10-03)
This prior offline slice used protocol 4, AvatarBridge 2 and WorldBridge 7. Its frozen receipt is historical source/build evidence; the subsequent dropped-friend slice used v5/2/8; its evidence is recorded above. Historical v3 native receipts below do not certify either candidate. The 76-byte outbound AvatarState/recording layout and v3 HitClaim shape remain unchanged; AvatarRelay is now an exact 84-byte connection-tagged envelope.
The frozen pre-change header repro refuted sequence-reset starvation: sequence 1 with a newer timestamp was accepted. It did reproduce retained interpolation (old position 10, replacement 200, midpoint 105), retained poses on unnotified roster removal, and timestamp rejection for equal-time replacements. That was actual header execution over synthetic samples, not a native reconnect failure.
The relay now stamps the sender's full connection ID and slot. NetworkClient requires a valid host/self roster and exact current owner ID before callback or loss accounting; duplicate/reordered sequences are rejected within a connection. Removal/replacement resets that stream; host/self/session/transport changes reset all. Malformed or invalid SessionState delivers an empty callback, so runtime and DLL caches retire immediately. Conditioned queues, clock state and loss windows cannot carry across a transport reconnect.
AvatarSync retains the admitted identity with each buffer and sampled target. Runtime publishes it with its receiver generation/local slot/self/host binding. The DLL checks cached provenance on admission and every drive use, including unchanged/torn bridge reads. Explicit Standalone poses require a positively Off, never-armed bridge; unknown or disconnected networking never means Off. Previous drive state is tracked independently for release. Companion restoration requires fresh repeated native friend-slot agreement; clone restoration reuses the existing complete canonical census. Both require checked bound/current metadata, fresh canonical-player exclusion and immediate lifecycle agreement. Uncertain restoration is withheld; native teardown forgets old pointers.
Release and Windows ASan passed 144 avatar, 148 world, 85 fake-simulation,
54 bridge, 71 production consumer/context, 125 trace and 166 damage-policy
checks each. Runtime/relay/avatarctl and both DLL builds passed. The avatar
controls exercise real ENet receiver admission with synthetic relay packets,
production relay slot replacement, lower-time/sequence-1 admission, old-packet
rejection, malformed roster invalidation, cached provenance, conditioned
reconnect and v3 rejection. The 71 consumer controls include 17 new authority
capture/current-membership guards over owned memory and mocked transport.
The initial fake-simulation failure was its hardcoded server=3 rejection
expectation; it now uses PROTOCOL_VERSION and retains the original rejection
assertions. The failure log is preserved. No sanitizer finding was reported.
Evidence is in build/rig/avatar_reconnect_offline_receipt_20261003.json,
avatar_reconnect_control_results_20261003.json, per-suite logs and two reviews.
These checks do not execute production EntityHook driving/restoration or native
KH2 callbacks. Connection provenance is not native actor incarnation. Same-room
source freshness still uses existing world/room/timestamp semantics; no full
room program/epoch tag was added. Explicit NetworkClient::disconnect retains
its no-callback contract, so callers must retire their consumers (runtime does
so at shutdown). No candidate was installed live, no save/gameplay action ran,
and reconnect/resync/native acceptance remains open on VUH-1508.
Offline relay session regression (2026-10-02)
kh2coop_world_test exercises real ENet loopback peers without KH2. Hit claims
require an established nonzero room epoch matching the host's current epoch;
the relay stamps the verified sender slot. No-room, zero, future and old-room
claims must not reach the host. Valid current claims retain their payload and
reach only the host. These are transport checks, not native damage application.
CampaignCoop host loss now ends all old peer connections. Both clean departure and host heartbeat expiry must clear membership, actors and cached world/ progress state while keeping the relay listening. Controls preserve a friend waiting before the host joins, keep friend heartbeats active while the host goes silent, and separately expire all peers to test notification deduplication. Fresh joins must receive no old room, hold, manifest, HP, death, progress or activation state and can start a new epoch/progress version. PublicRealm is outside this co-op teardown change.
cmake --build build --config Release --target kh2coop_server kh2coop_world_test kh2coop_fake_sim kh2coop_avatar_test kh2coop_bridge_test
./build/Release/kh2coop_world_test.exe
./build/Release/kh2coop_fake_sim.exe
./build/Release/kh2coop_avatar_test.exe
./build/Release/kh2coop_bridge_test.exe
The new controls reproduced six hit-epoch failures and seven host-loss failures before the respective fixes. World/fake/avatar/bridge suites passed 111/78/65/44 checks on Windows, then passed with Windows AddressSanitizer. Portable world and fake suites also passed ASan+UBSan under Linux. Avatar/IPC tests remain Windows-only. Current evidence is recorded in the relay lifecycle receipt. Native combat, gameplay disconnect/reconnect and remote-session gates remain separate live requirements.
Automatic client hit claims: offline candidate (2026-10-02)
Ordinary local-player HP hits now pass from the native client hook through the runtime and relay to the host's byte-verified TakeDamage callback. Protocol v3 adds typed targets and relay-issued connection IDs; WorldBridge v6 publishes atomic roster IDs so disconnected or replaced senders cannot keep queued authority. A claim echoes its original connection, uses a strictly increasing sequence across rooms, and is consumed before one native attempt. Old protocol and legacy handshakes, duplicate identities and wrong room/target claims fail closed. Native calls require current checked combat metadata and a complete census; each call is followed by another census before HP/death/hash publication.
Release DLL/runtime/relay builds passed. Native-consumer/world/bridge/fake/avatar
suites passed 42/148/54/85/65 checks on Windows, then with AddressSanitizer.
The actual consumer and publisher run over owned synthetic memory in the native
suite: faults, recursive callbacks, expiry, retired connections, metadata
replacement and post-call list/lifecycle changes are exercised. Positive decoded
hash controls establish real post-callback HP, removal and replacement behavior
in that fixture; unavailable post-census cases publish no stale state. This
tests production EnemySync, with mocked transport/lifecycle/progress services;
it does not run native KH2 functions or the EntityHook ownership resolver.
Portable world/fake suites passed 148/85 checks with ASan+UBSan. Native SEH
and IPC suites are Windows-only; Windows UBSan remains unavailable. The native
consumer test compiles with /W4 /WX; portable C++ builds use
-Wall -Wextra -Wpedantic. Only existing CMake dependency/deprecation notices
remain. Receipt and logs: D4 offline receipt.
net_client_hit_claims.json was prepared and syntax-validated offline. It starts
with nonempty typed enemy parity, drives real combos from both clients without
host attack/manual damage inputs, correlates immutable claim IDs to unique host
attempts, requires an exact nonlethal HP decrease and then strict native applied
HP/death/progress parity. Its first native run passed as recorded below. An unmatched courtyard/refill
population remains a failure, rather than a relaxed readiness condition.
Boss finishers, survival/caps, attack-specific effects, indirect attack ownership and foreign-thread damage are outside this first automatic path's evidence. Calculated damage is sent before native survival adjustment; the host does not replay the full attack record. Native creation identity remains D5 work. No live game/process/bridge actions or disk-save operations were performed for this offline validation; the subsequent live result is recorded below. The existing native-wave failure remains open.
Automatic client hits: native courtyard acceptance (2026-10-02)
20261002-235208_net_client_hit_claims_1
passed all 49 steps in 150.4 s, through the existing Session 1 desktop
bridge. DLL SHA-256 was
37C4A724CB5388FBC66DC7560A9DE9ADD1E88835FA742E46000C7A4426FCBE81;
runtime and relay used matching protocol v3 and WorldBridge v6. The host had
no attack inputs or manual damage commands. Each client performed ordinary
native combos in turn.
The claim audit
correlates 22 published claims → 22 host receipts → 22 unique native
attempts, with matching immutable payloads and no duplicates, missing
attempts, rejected claims or unknown native outcomes. All attempts recorded
nativeOk=1 and afterHpAvailable=1: 19 exact nonlethal HP reductions and
three lethal reductions clamped at zero. Client 1 published 12 claims;
client 2 published 10, including the three lethal claims on netIds 2, 1 and 3.
Each corresponding death was applied exactly once on each client, with
positive pre-HP and observed post-HP zero; no failed death applications appeared.
Two fresh matching samples at every checkpoint showed epoch 1, full location
[5,6,0,1,1,0], zero unmatched actors and equal recomputed native enemy hashes.
Five object-302 Shadows began at 20 HP. After client 1, HP was
[5,20,8,9,13] by netId; after client 2, only netIds 4 and 5 remained alive
at 9 and 13 HP everywhere. The shared progress hash stayed 2596620699.
All four disk-save hashes were unchanged, with no save violations.
This establishes ordinary client-only Shadow damage and kills in this fixture. It does not establish boss finishers, survival/caps, attack-specific effects, enemy-to-player authority, native reconnect, general wave convergence or full barrier/reward parity. The fixture does not force a lethal outcome; the three observed kills are supported separately by the audited native death logs. The captures are settled stills, not motion clips.
Incoming player damage: remaining acceptance
The client-kill fixture protects all three Soras, so its pass cannot establish enemy attacks on a client player. The current step-1 enemy code keeps local native AI on every peer and passes native damage to Sora through the existing hit path. Historical Shadow-to-Sora logs establish that path, but lack a current registered-client and canonical-victim join. They do not prove that AI-suppressed enemies generate hitboxes from mirrored host motion in step 2.
A victim test needs a fresh client Sora that was never protected, a completed connected room, one genuine nonlethal native enemy attack, and checked canonical-player/head/status/HP observations. No manual damage command or HP write supplies the hit. Static slot-0 HP or an actor name alone is insufficient to identify the controlled victim. Existing hit/HP logs are capped at 60/40; missing records do not establish absence, and matching frames do not establish one unique causal hit scope. Full damage-rule acceptance still needs source, scope, connection/epoch and actual adjusted-delta/HP evidence, plus rejection of damage to remote puppets. No new victim fixture has passed live.
Offline investigation: native contract and archived diagnostic coverage.
The default-off native observer is enabled by setting KH2COOP_TRACE_HITS=1 in the launched game's environment. It wraps the three existing guarded damage hooks without changing their calls, arguments, returns or authority filters. Snapshots join the current connection IDs, generation, epoch, load, transition and full room tuple to checked canonical player/head/tracked pointers, source metadata and pre/post HP. An ordinary incoming client-local witness requires one normal ApplyHitDamage scope from return site 3D613C, one nested TakeDamage/ApplyStatDelta chain, and agreement between the genuine adjusted negative delta, Stat result and checked HP/clamp. ApplyHitDamage's raw RAX is never treated as success.
Capture is bounded to eight children, eight nested parent scopes and 128 queued events; the frame drain emits at most 16 events per call. Partial installation, foreign calls, unreadable or changed facts, nesting, overflow, unwind and lost observations stay unavailable or ambiguous. Source names retain the existing F_ exclusion; they do not establish ownership. Client Sora's identity requires the canonical roots, rather than the name or slot. The room/session capture reads owned state and fault-contained native memory; its host-arrival check has no warp-channel writes.
The saved-log auditor reports this schema separately under each log's nativeHitTrace field. Read complete and witnessCount there; the existing CLI exit code still describes the combined spawn/lifecycle envelope. A hit-only log can therefore have a complete native-hit section while returning exit 1 for absent spawn/lifecycle traces. Historical logs without hittrace schema remain unavailable. A witness establishes that observed ordinary local-AI damage scope only: no boss behavior, attack lifetime/incarnation, full remote-puppet veto policy or step-2 host-replicated hitbox acceptance. The new candidate has not been installed or tested live.
Release and Windows ASan passed the new trace core's 92 controls and the production claim/context fixture's 51 controls; spawn 75 and lifecycle 59 regressions also passed. The Python auditor passed 85 controls. Both compiled trace executables emitted a synthetic baseline through the actual production serializer, and the auditor independently accepted its checked 100-to-93 HP change with adjusted delta -7. Six serializer/auditor controls passed. This uses synthetic originals and owned facts, rather than installed game hooks. Independent reviews accepted the adapters, core, context and auditor after replacing the warp helper that wrote channel status. The auditor deliberately withholds full-file certification for any unknown scope or nonzero coverage counter, including unrelated out-of-scope Stat calls. Such a result does not establish absence of incoming damage. See the native-hit offline receipt.
Active-session HP ownership gate: offline candidate
The separate DamagePolicy gate runs without the trace opt-in. Its supported boundary is an available current Host/Client session on the registered game thread, with a readable, unapplied, nonzero stat-0 HP record. In that boundary, remote representations and noncanonical player actors cannot receive or cause HP changes; unknown source ownership also suppresses HP. These exclusions take precedence over permission for the canonical local victim.
| Supported victim | Supported source | Result |
|---|---|---|
| Canonical local avatar | Positively classified native actor | Existing native damage |
| Host enemy | Canonical host avatar or positively observed native companion | Existing native damage |
| Client enemy | Canonical client avatar, positive damage, checked non-healing kind | One existing claim attempt, then zero HP amount |
| Any other pairing | Any | Zero HP amount, no new claim |
Off/foreign calls, unavailable context, unreadable hit records, applied records, non-HP records and zero amounts preserve the previous filters and native path. Unavailable context is distinct from unknown ownership inside a valid context. Healing kinds 5/6 never become claims; remote HP suppression covers signed nonzero amounts. The final zero leaf rechecks exact flags/stat/amount and writes only the calculated damage field. Changed or unreadable observations do not certify a veto. ApplyHitDamage is still called once with the original arguments and genuine raw return, preserving native consumption and effects. No second damage call, direct player HP write, new wire message or fabricated manual claim was added.
Positive companion permission comes only from the existing original FriendAI branch, with checked handler, current frame/session/roster and actor metadata. Fresh native friend pointers must agree with cached targets at capture and use; cached puppet drivers and currently reserved remote targets stay excluded. The independent review found and closed the stale raw/cache replacement gap. These checks are conservative current-control evidence, not authenticated remote incarnation or native actor lifetime proof. Other checked native source classes can affect only the canonical local victim. The selected matrix also suppresses supported HP records for other victims, including companion/world-object candidates; compatibility outside ordinary enemy/Sora fixtures is unverified.
Release and Windows ASan passed 166 policy/owned-record controls and 54 production claim/context controls. Both DLL builds passed after the companion membership fix. The policy test executes the actual copied-facts core and checked zero leaf; its original/claim harness is synthetic. It does not execute the production membership adapter, native callbacks or installed hooks. The context fixture tests full-width roster IDs and replacement between reads. Unchanged trace/auditor evidence above remains valid. No candidate was installed live, and neither remaining VUH-1503 acceptance criterion is closed. See the policy review and policy offline receipt.
Recorded ownership decisions and checked-zero outcomes
With KH2COOP_TRACE_HITS=1, each captured Apply scope now carries an optional five-row damagepolicy schema-1 envelope using that same sequence. It records the gate's own copied authority context, victim/source and hit facts, all three full-width roster IDs, evaluated action/reason, and actual claim-attempt/queue, revalidation and checked-zero outcomes. A queued claim is not host acceptance. Unattempted result fields are placeholders. No extra native reads, damage calls, wire messages or authority changes were added to collect these facts. Historical hittrace schema-1 output without a policy envelope is retained.
The saved-log auditor reports damagePolicyTrace separately. Its decisionCount checks the copied decision against the frozen matrix; its zeroVetoCount requires much more: a directly checked noncanonical type-0 player exclusion, matching authority/enclosing pre/post facts, complete normal hook coverage, recorded successful revalidation and Zeroed outcome, native record consumption, unchanged checked victim HP and no nonzero HP child delta. Zero-delta Stat can return zero without writing HP; its checked HP and ancestry remain required. Fault/Changed, failed revalidation, manual/sync-only zero and a producer action alone do not certify a veto. Read event limits even when the recorded envelope is complete.
NativeHitTrace's ordinary incoming-witness checks remain unchanged. A veto is an expected non-witness there, so incoming witnessCount can be zero while the separate policy envelope has bounded veto evidence. The CLI still reports the combined spawn/lifecycle/hit provenance and can return 1 for a policy-only log. Neither section proves an absence of attacks. The policy audit independently compares the two pre-captures and raw post-capture; revalidation/Zeroed remain recorded execution outcomes. There is no logged third-roster post snapshot. Driver/native-AI stamps and authenticated peer incarnation cannot be reconstructed from these rows. Type-0 exclusion is not authenticated remote-human ownership.
Release and Windows ASan passed 125 trace/serializer controls; both DLL builds passed. The Python auditor passed 107 controls. Both real serializer executables retained byte-identical historical baselines and emitted two synthetic veto scopes independently accepted by the policy audit. Four actual-format mutations (fault result, failed revalidation, truncated host ID and mismatched canonical root) produced no veto evidence. The emitter uses the production policy and owned-record zero leaf with synthetic original callbacks and copied membership facts. No production EntityHook callback or native gameplay proof is claimed. Current source/build/test hashes and review are in the policy-observation receipt.
What a run does
- Takes the rig lock (
build/rig/rig.lock, holder pid and command). One live lane at a time: a second runner exits with code 3. A lock left by a dead process is stale and taken over. - Refuses if a KH2 the rig didn't launch is running (exit 3): James may be playing. It never touches that process.
- Hashes the save folder (SHA-256 of every file under
My Games\KINGDOM HEARTS HD 1.5+2.5 ReMIX\, read-only) before and after the whole suite. Any change other than Steam's ownsteam_autocloud.vdffails the suite (saveViolation). - Runs each scenario's steps in order; the first failing step ends that run.
While a step runs, every instance is watched:
- process exited → CRASH: the bundle has the DLL's minidump
(
kh2coop_crash_<pid>.dmp, written by its unhandled-exception filter) and the inject log; - gameplay frames stalled for 30 s in the field → HANG: the runner
writes an outside minidump (
kh2ctl dump) and kills the instance.
- process exited → CRASH: the bundle has the DLL's minidump
(
- Kills every instance the run launched, pass or fail, and moves on to the next scenario.
Output goes to build/scenarios/<stamp>_<name>_<attempt>/ (report.json,
report.md, captures, clips, logs, bundle). build/scenarios/last_suite.json
holds the suite summary. Exit codes: 0 all passed, 1 a scenario failed or the
save changed, 2 a scenario crashed or hung, 3 rig unavailable.
Save safety
Three layers:
-
Pre-boot launch protection (2026-10-07 candidate):
kh2ctlcreates KH2 suspended and injects before the main thread runs. It resumes only after the DLL acknowledges that the save guard and hook initialization succeeded. An old DLL, incomplete guard, missing acknowledgement or launch failure terminates the new owned child;--no-injectis refused. This closes the earlier window-before-injection gap. Revision 3 adds explicit DLL acknowledgement (PID/QPC before SetEvent), launcher observed-wait/resume QPC receipts in JSON and its own log, and a first hooked Present receipt. The live judge requires matching PIDs/log objects, strict ack-before-resume QPC ordering and the installed-guard line before first Present. Offline controls and limits are inbuild/rig/vuh-saveguard-preinject-20261007-01/; live qualification is PENDING. -
The DLL's save guard (
inject/src/SaveGuard.cpp) is installed first at init and covers everything under the save folder from inside the game.- Write opens are redirected. A write-capable open through KernelBase
CreateFileW/AorCreateFile2goes to the same relative path underbuild/rig/logs/save_sandbox_<pid>/. If the open keeps contents (r+b), the original is copied in first. After that, reads of that file go to the sandbox too, so the game sees what it "saved". - Why redirect rather than deny: the save writer doesn't check
_wfopen_sfor NULL before libpng, so a denied open would likely crash the game mid-save (repos-60's static read). - Everything else is denied: copies (
CopyFileA/W/ExW/2), moves (MoveFileExA/W,MoveFileWithProgressW),ReplaceFileW, deletes (DeleteFileA/W) andRemoveDirectoryA/W. Copy goes to NtCreateFile without passing CreateFileW, so it's hooked by name. If no sandbox can be created, write opens are denied too (fail closed). - Logging: every operation is logged as
[saveguard] redirected|blocked …. - Self-test: setting
KH2COOP_SAVEGUARD_TEST_DIRbeforekh2ctl launchguards that directory too and runs a self-test at init. It plants a probe file there through the unhooked API, then tries every path against it. That includes_wfopen_scalled through the exe's own import thunk (RVA0x471BC8), firstr+b(which must read the original), thenwb, and theDeleteFileAthe exe imports. - Result on 2026-10-02: 0 of 14 calls reached the probe. Opens were
redirected, and
r+bread the original. Everything else was denied.
- Write opens are redirected. A write-capable open through KernelBase
-
How the game writes saves (headless Ghidra, 2026-10-02):
- The save path comes from the string
KINGDOM HEARTS HD 1.5+2.5 ReMIX\Steam\KHIIFM_WW.png(RVA0x7164E0). FUN_1400fef80opens the save container read-only (CRT_sopen_dispatch). If the file reads as empty, it callsDeleteFileAon it.FUN_140100810creates the folder, checks free space and writes the PNG throughFUN_140145170. Boot invokes it when decoded payload size is zero; a nonempty container is therefore not sufficient protection.FUN_140101000also invokes the deleting helperFUN_1401007a0after initialization errors or a decoded account/header mismatch. These are conditional boot paths, not proof that a valid existing PC1 save is mutated.- The PNG writer
FUN_140145170opens the file with ucrtbase_wfopen_s(path, L"r+b"), thenL"wb". Both reach KernelBaseCreateFileWwith write access, which the guard redirects. - The game changes its working directory (
_chdirinFUN_140101000), so the guard resolves relative paths to full paths before matching.
- The save path comes from the string
-
The suite's before/after hash, which catches anything the guard misses.
Never save in-game without James's explicit approval.
Writing a scenario
{
"name": "sora_move_attack",
"description": "What it shows, in one sentence.",
"protect": [0],
"steps": [
{"do": "boot"},
{"do": "warp", "world": 5, "room": 6},
{"do": "save", "as": "start", "expr": "pos()"},
{"do": "input", "ly": 1, "ms": 900},
{"do": "assert", "expr": "dist(pos(), saved['start']) > 300", "message": "Sora didn't move"},
{"do": "press", "button": "cross", "times": 20, "gapMs": 300},
{"do": "capture", "name": "after_combo"}
]
}
"instance": N (default 0) on a step picks the instance; boot/launch
append a new one. "protect": [N] keeps that instance's own player on team 0
(no ordinary attack hits it) for the whole run, so combat rooms don't end in a
game over. It is a single-PC fixture aid for an unplayed stand-in friend; the
two-PC route uses none, and reports say "stand-in friend protected". Each
second the protector writes team 0 to the actor that instance's camera follows
(Roxas P_EX110 in the TT prologue, Sora later; never a remote player's clone).
A pilot-played instance is never protected: pilot refuses a protected seat
and protect refuses a piloted one. Scripted boss hits can still reach team 0
(Axel, VUH-2364). The one exception is explicit: {"do":"protect","afterPilot":true}
protects a seat whose route pilot has finished, for a later bot fight only
(VUH-2901). It refuses a seat that was never piloted or whose background pilot
is still running, and pilot still refuses it while it is protected, so no
pilot ever plays a protected seat. Reports then say "stand-in seat protected"
and name the seats protected after their pilot. Why it is allowed (lead-approved
on VUH-2901): the rule exists so that a route proof is never made under
invulnerability, and that still holds. After its route, a piloted seat can be
the only one left fighting, like friend_down's host while the friend is
forcibly downed. Without protection there, a bot losing a solo fight ends the
run in a native game over before the claim under test (the downed seat's
return) is due. Use it only over a window whose claims do not include that
seat taking hits, and say so in the scenario.
{"do":"protect","on":false} only stops that polling; it does not restore
the previous team value. An incoming-damage test must use a fresh naturally
damageable Sora and verify its canonical identity instead of reusing a
protected actor. Protection is best effort during loads.
| Step | Fields | Does |
|---|---|---|
boot | timeoutSec initTimeoutMs slotMoves newGame | Launch + inject, mute, pick LOAD on the title menu (checked by pixel in captures), load the save list's default slot (or slotMoves rows from it: + down, − up; the cursor is kept as save_list_N.png), wait until the room is live; newGame: true instead presses NEW GAME and returns at the Game Mode menu (VUH-2338: Standard Mode and vibration ON are the default rows, then "Begin with these settings?" Yes; the first menu ignores input for about 7 s). Menu presses go through the DLL's input collector (player-press), so no window focus is needed |
pilot | route fromStep teach seconds name allowStuck instance input background | VUH-2340 route pilot (tools/rig/route_pilot/pilot.py) plays the instance from its key map. input is pad by default: the keys become slot-0 pad state in the existing inject input mailbox (the kh2ctl player-input channel), and the game never takes focus. keys sends real scan codes to the focused window and steals focus before each key, so use it only when nobody is at the PC. It plays a route file of data steps, or teach commands from a mailbox directory. Its output and clips go to <name>/; key-ups are re-sent even if it is killed. Exit 3 (stuck) fails the step unless allowStuck. With background the step returns at once and pilot_wait collects it, so two seats can play a route at the same time (VUH-2466). |
pilot_wait | none | Waits for every background pilot in start order, releases its keys, and fails as that pilot would have; the others are still collected first. |
launch | mute initTimeoutMs | Launch + inject only (stays on the title). initTimeoutMs is an integer1..60000, default15000, passed to the CLI's existing hook wait. A valid returned launched PID is registered for owned cleanup before initialization failure is raised; failed initialization never advances into boot/input. Missing/malformed PIDs do not supply a cleanup target. |
warp | world room door map btl evt | kh2ctl warp; returns once the room has loaded |
input | lx ly rx ry ms | Hold sticks (player-input) |
walk_to | x z instance timeoutMs until as | Steer with the current camera basis in 200ms native pulses. Record start/end displacement and peer enemy lists in JSON. Stops near the target, at until, or at the time budget; reaching the budget makes no cull claim. |
align_courtyard_exit | instance as blockedExpr | BC courtyard entrance-0 fixture only: pulse screen-left (lx=-1) for 250 ms at a time, recording each start/end position. Stop at abs(x)<60 or a true blockedExpr, with a 6 s limit. Each unblocked pulse must reduce abs(x); otherwise capture and fail with a diagnostic JSON. No host movement |
press | button ms times gapMs | Press a button (player-press; PS2 names: cross, circle, triangle, square, start, …) |
fight | instances seconds reach button as clip until | Bot combat: each listed instance's own Sora walks at its nearest living enemy (camera-basis 200 ms pulses) and presses button (default cross) within reach units (default 250), all instances at once, until seconds pass or it sees no enemy. clip: {instance, seconds, fps, name} films the fight while it runs (clips films several at once). sample: {expr, everyMs} evaluates expr while the bots fight and saves the series as <as>_samples. Optional until stops all bots when the expression becomes true, retaining the seconds deadline and waiting through empty wave gaps. <as>["until"] records whether it was reached; assert it or the live condition separately. Records presses, pulses, closest distance and enemy counts; asserts nothing |
checkpoint | name | Opens a route segment (see "Route runs" below) |
wait | ms | Sleep while watching the instances |
wait_until | expr timeoutMs pollMs | Poll an expression until true |
assert | expr message | Fail the run unless the expression is true |
save | as expr | Store a value in saved[...] |
protect | on, afterPilot | Start/stop team-0 protection polling; stopping does not restore the previous team. afterPilot: true is required for, and only accepted on, a seat whose route pilot has finished (VUH-2901) |
capture | name | PNG from inside the renderer; retain the actual CLI response under receipt alongside the relative artifact path |
clip | name seconds fps background | MP4 → artifact with full CLI receipt and adjacent .receipt.json / .capture-time.jsonl sidecars. background: true films while the next steps run; clip_wait collects every background clip (do it before a capture on that instance) |
clip_wait | Waits for the background clips and records their receipts | |
avatar_stall | none | Two-player net_avatar_stall.json: suspend the original sender runtime handle for 2 s, move its Sora 150 units in RAM, resume in finally; both games and viewer keep running. Six-second viewer clip and bridge samples check active/held/live states. Native [ptrace] logs retain per-frame hold and resume positions |
crash, freeze | Runner self-tests: fault or suspend the instance | |
relay | port build content mod args | Start kh2coop_server on loopback for this run; the version gate defaults to what the runtime sends |
runtime | instance role peerId link args expect timeoutMs | Start kh2coop_runtime_scaffold --network --no-camera --pid <instance> and wait for expect in its log (default Verified membership; native bootstrap remains separate). Refuse an existing owned runtime/log or live owned writer for the same game PID before opening the log. link: {latencyMs, jitterMs, lossPct} sets the runtime's impairment both ways (owner→viewer crosses two runtimes, so 50 ms each = 100 ms) |
reconnect_mark | as instances timeoutMs | Seal current original [0,1,2] process/session/connection/generation/native-load/provenance observations and exact log prefixes; default timeout10s, maximum15s. Preserve raw inputs on success or failure |
walk_to options (VUH-2466) | body radius | body: "self" walks this instance's canonical player (0x2A105D0), whatever its body, and ends quietly when there is none (an event); radius is the stop distance (default 100); until is checked before the first step too |
fight option (VUH-2466) | types | Object types the bots target (default [3, 4]; add 9 for a BTLNPC duel such as the Day 1 Sandlot's) |
runtime_stop | instance name | VUH-1512: drop one seat's connection by terminating that instance's owned runtime (name, default runtime_<instance>), leaving its game running, as a lost link or the launcher's Disconnect fallback does. A later runtime step with its own name (for example runtime_1_rejoin) reconnects the seat |
runtime_pause | instance:1 after as timeoutMs | Suspend only the original registered Friend1 Popen handle after a qualified reconnect baseline. Wait for fresh exact retirement/survivor evidence, then resume the same process. One pause per baseline; timeout1–20s; retain recovery receipt in finally |
reconnect_check | after as timeoutMs | Require the original successful pause receipt and original live process/relay bindings, then strict same-session native recovery against the baseline. Default45s, maximum60s. Retain raw/partial inputs; repeat with a distinct as after final population gates |
record / record_stop | as instances | Sample every party actor's position (with the instance's world/room) on those instances in the background; record_stop writes <as>.csv |
wander | instances seconds seed | Seeded random stick walks with occasional jumps, taking turns (soaks) |
hit_all | instance op amount | kh2ctl hit damage/kill on every live combat enemy of that instance (host-only damage tests) |
clear_host_enemies | timeoutMs emptyMs pollMs as | Kill only host live combat enemies until continuously empty for emptyMs; poll for delayed waves within timeoutMs (maximum60s). Defaults30s/3s/500ms. Preserve every sample and hit count in <as>.json, including on timeout. Follow with a separate all-peer empty assertion |
kh2ctl | instance args or argsExpr | Run the supplied argument array against that instance. argsExpr evaluates an expression to an array, for explicit reversible fixture writes |
transition_check | as after instances target timeoutMs capture | Wait for completed-load parity, relay ACKs, and restored puppet slots; save structured evidence under as (default transition). after names an earlier checkpoint whose epoch must advance; target is the expected [world, room]. Default instances are [0,1,2], default timeout 30 s. On timeout, capture every participating instance and preserve the last observation as <as>_failed.json. capture: true also captures successful checkpoints |
| progress_snapshot | as instances compare | Save actual allow-listed progress and complete personal byte ranges to JSON. Optional compare names a prior snapshot and emits each changed SAVE offset and before/after byte |
| statehash_check | as instances minEnemies timeoutMs | Require fresh post-arrival hashes on all peers at the same completed epoch and exact six-field location, nonempty native enemy rows, zero unmatched actors, and matching netID/object/HP populations. Save evidence or a failure artifact |
| dismiss_goa_map_reward | as instance | With flag409 open in GoA, capture the reward popup and poll the unchanged native safe gate; while unsafe, send cross150ms and wait700ms, bounded to10s. Save every gate sample, press count and failure capture |
| approach_goa_chest | as instance | In GoA on the central platform, walk toward flag409 using camera-basis projections and 200 ms native pulses. Stop within 100 units, require progress over each three-pulse window, fail after 15 s with a trace/capture |
| courtyard_diagnostic | as instance baseline timeoutMs | Replay the original courtyard client pulse/combo after a positive hash baseline; capture player/camera/spawn bindings and require a persistent extra unmatched actor plus enemy-desync. No reproduction fails explicitly as INCONCLUSIVE; a room/epoch change invalidates the diagnostic |
| enemy_hash_control | as instance | Separate native enemy fault: temporarily set one matched client's per-actor HP-lock bit, kill its host counterpart through the native hit path, require failed client death and two fresh native hash disagreements with relay enemy-desync, and restore only that bit if actor identity still matches |
| progress_hash_control | as instance minEnemies timeoutMs | Outside GoA, after a successful progress apply, toggle only chest flag 409 on the selected rig client, verify readback and fresh relay fields=4 evidence, then restore and verify the original byte in finally |
| progress_fault_resync | Fixed instance:0 slot:all as:forced_resync | With the exact qualified five-Shadow HP17 courtyard setup, fault only Friend1 flag409, retain full native progress/personal reads and two progress-only mismatch frames, then run one strict both-target resync. Require actual one-byte repair before cleanup; preserve original transaction artifact for unchanged postchecks. Non-atomic diagnostic reads/write, with observed context guards and safe masked failure cleanup |
Processes started by relay/runtime are stopped at the end of the run.
Expressions are Python with these helpers (the instance index is the last
argument, default 0): room() → (world, room); pos(name='P_EX100') →
(x, y, z); actor(name) → the kh2ctl entities record (hp, maxHp,
team, motionId, address, …); actors(); enemies() (objentry type 3/4);
peek(rva, kind='u32'); peek_at(address, kind='u32') (an absolute address
inside the module image, such as an actor field: address + 0x4D8); log_count(text) (lines in the inject log, e.g.
'attacker=P_EX100' from the hit log); dist(a, b); saved;
enemy_hps(i) (sorted (name, hp) of live combat enemies); bridge(i) (the instance's AvatarBridge via avatarctl peek: local
frames/s and both puppet slots); puppet_error(saved['rec'], owner, viewer).
location(i) returns [world, room, door, map, btl, evt] from one NOW sample;
log_matches(pattern, i) returns named regex captures from the inject log;
runtime_log(i) returns the runtime log. int and str are available when
working with captures and structured evidence. chests(i) resolves native F_*
actors through their bounded actor+0xC00 treasure pointer, validates the
record against the current world/room, and returns native actor state with
its treasure/item IDs, overall completion flag, SAVE byte and opened bit.
It requires the CLI's peek.moduleBase metadata; invalid reads fail explicitly.
puppet_error scores how well the viewer's puppet follows the owner's
Sora in a recording:
- Each sample from a viewer actor (other than the viewer's own Sora) is compared with the owner's positions over the preceding 0.5 s, because the puppet renders behind on purpose. The best-matching actor counts as the puppet.
- It returns
mean,p95andmaxerror in KH2 units (100 = 1 m), the fittedlagMs, andownerTravel. - Check
ownerTraveltoo. If the owner barely moved, a low error proves nothing. - Only samples taken while owner and viewer are in the same world/room count. Puppets are matched by actor name, since addresses repeat across rooms.
jitter_stats(saved['rec'], owner, viewer, err['actor']) counts "pops": puppet
steps between consecutive samples that exceed the owner's largest step over
the last 0.5 s by more than 30 units. Steps only count while the owner has
been in the viewer's room for at least 1.5 s.
tools/scenario/spikes/pops.py RUN_DIR OWNER VIEWER NAME lists each pop
with its context. The sampling runs at about 20–40 Hz per instance, so
single-frame jitter needs a clip.
Capture-time receipts
kh2ctl clip retains the complete CLI result in <clip>.receipt.json and one
native timing row per frame in <clip>.capture-time.jsonl. Rows identify the
frame, Present sequence, QPC frequency and bounds of the Present callback that
submitted its pixel copy. These are capture-time bounds, independent of delayed
GPU readback and MP4 encoding; they do not measure physical monitor scanout.
The receipt also retains the measured Present rates before and during capture.
The scenario runner exports both sidecars for foreground, background and fight
clips.
For same-PC seats, use tools/scenario/clip_timing.py with the two receipts,
zero-based first-visible-defeat frame markers, --same-pc and --out. Review the
preceding frame on each seat to establish that defeat was not yet visible.
Strictly decode each MP4 and match its decoded frame count to the receipt before
using those markers. Missing/corrupt media cannot qualify a timing result.
The tool checks complete monotonic timing rows, different seat PIDs and matching
clock domain/frequency. It bounds onset between the preceding non-defeat capture
and first defeat capture, and passes only if the entire absolute skew interval
is within1000ms. A boundary gap or interval crossing that threshold remains
UNQUALIFIED. Cross-PC QPC clocks cannot be compared with this tool.
Example scenarios
tools/scenario/scenarios/:
boot_to_goa: load the save, warp to the GoA, Sora moves.sora_move_attack: BC courtyard, run in, combo until Sora's hits land in the hit log.two_instances_same_room: two instances load and warp to the GoA; both are there and both Soras move.- VUH-1492 networking (relay on loopback, one runtime per instance):
net_two_instances,net_two_instances_impaired(100 ms + 2% loss),net_three_instances: every puppet follows its owner.net_room_hide: a puppet hides while its owner is in another room.net_mismatch: a mismatched peer is refused with a reason.net_viewer_leaves: the viewer changes rooms with a puppet active.net_soak_10min+soak_control_no_network: the 10-minute soak and its no-network control.
- VUH-1502 enemy sync (host + 2 clients):
net_enemy_sync_courtyard,net_enemy_sync_waves. - VUH-1496 host-led transitions:
net_host_transitions_smoke,net_host_transitions_acceptance, and the calibrated supplementarynet_host_transitions_native_exitfixture (details below). forced_crash,forced_hang: runner self-tests. They're expected to report CRASH/HANG with a bundle; don't include them in a pass/fail suite.
Host-led transition evidence (VUH-1496)
Run the smoke first, then the route, from the desktop live lane:
python tools/scenario/run.py tools/scenario/scenarios/net_host_transitions_smoke.json
python tools/scenario/run.py tools/scenario/scenarios/net_host_transitions_acceptance.json
Both fixtures boot three games before starting the runtimes. The host starts
in GoA, client 1 starts in a different room, and client 2 connects only after
the host and client 1 have completed another transition. The late join must
arrive and acknowledge the existing host epoch. Session slots must equal
instance indices: start the player, friend1, and friend2 runtimes in that
order. Boot uses the save list's default slot, so the room programs and party
availability still depend on that save.
The acceptance route then performs 20 host warp requests across five rooms
(04/1A, 05/01, 05/04, 05/05, 05/06). All three Soras are protected
in these fixtures. Each checkpoint requires:
- A new completed-load epoch on all three instances, not just matching IDs.
- Exact equality of world, room, entrance, map, battle and event programs between every current NOW sample and its arrival log, and between peers.
- A successful
TransitionAckfor each client's current epoch and room in the relay log. - A live local avatar, both active puppet poses with the expected distinct
remote owners and room, and both native friend actors within 100 game
units of their published poses. The latter is a settled-position check;
continuous motion fidelity remains covered by
net_three_instances.
Native actors are resolved against the active entity list. Companion targets
come from the two unit-slot-1 actor pointers (0x2A239B0, 0x2A239B8). With
non-head player-class actors present, the DLL selects Sora clones instead;
the checker takes the actual per-slot addresses from [puppet N] ... actor=
motion-driver logs after the latest [warp] load complete marker. Names or
list ordering do not establish clone ownership. Both targets must be distinct,
nonzero, different from the local actor and present in the current entity
list. Reports retain the pointers, resolution source, candidate clone
addresses and resolved actors. A default kh2ctl state friend record is
insufficient: it can contain a zero position when no friend entity exists.
The first live smoke on 2026-10-02 reached matching epochs, locations and ACKs
in Twilight Town 02/00 but failed puppet-position checks. Its report remains
under build/scenarios/20261002-142449_net_host_transitions_smoke_1/. The
replacement route avoids that party-capacity fixture and avoids BC 05/02,
which can open a cutscene. The passing replacement runs below establish two
native companion targets in every route room for the current save; missing
native puppets continue to fail. The late
join checkpoint captures a successful sample on every instance, and every
failed checkpoint attempts captures before writing its diagnostic snapshot.
Passing live evidence, 2026-10-02 (WorldBridge v4).
| Scenario | Report | Result |
|---|---|---|
| Smoke | 20261002-143623 | PASS, 139.7 s. Initial host-follow, late join at the existing epoch, and a same-room reload with all three instances and restored native puppet targets. |
| Acceptance route | 20261002-143907 | PASS, 195.8 s. Twenty route loads at epochs 3–22 across all five rooms; every checkpoint has both client ACKs and six native puppet-position checks. Largest settled error: 37.57 units (limit 100). |
| Native walking exits | 20261002-145121 | PASS, 153.1 s. A client request for 05/00 was blocked at epoch 3; the host's native walking loads advanced through epochs 4–6 with both client ACKs and a final all-instance puppet check. |
All three runs checked four save-file hashes with no changes or save violations.
These fixtures used native Donald/Goofy companions, not replacement Sora
models. The final two acceptance loads had the exact location
[5, 1, 0, 0, 0, 20] with different completed-load epochs. Each report retains
the complete per-instance location, owner, native actor and ACK evidence;
the late join and end checkpoints have screenshots of all three games.
The last two loads have the identical six-field location. Their epochs must still advance. This exercises native request/load hooks via host CLI warps; it does not prove a naturally triggered same-room script reload. The smoke includes the same reload check with two route loads.
Arrival logs come from the native load lifecycle. Static tracing found that
RequestTransition (0x152990) stages the packet, commit (0x152BE0) writes
the entrance as one byte at NOW+2 and resolves programs, and the load task
(0x152680) schedules completion (0x152CD0) after the loaders. Completion
sets IN_FIELD and runs finalizers; the direct equivalent 0x152F40 is also
hooked. The request and both completion hooks check 24 executable bytes
before installation. Client acknowledgement waits for completion return,
the gameplay gate and all six fields to match. A pause/stall alone is not
load completion evidence. These implementation facts still require the
live scenarios to establish behavior on the running build.
net_host_transitions_native_exit is a supplementary walking fixture,
calibrated live on 2026-10-02. At BC courtyard entrance 0, client 1 first aligns toward the
doorway centerline using 250 ms screen-left pulses (lx=-1). Each pulse
records its start/end position and must reduce abs(x); a wrong direction
or obstruction captures and fails. Alignment stops at abs(x)<60, after a
new blocked native request, or fails with a capture at the 6 s bound. The
blocked-request baseline is recorded before alignment. The client then
holds backward for 4 s; the host is untouched until its own backward pulse.
It requires a new [warp] client native exit blocked log and then unchanged
full locations and host epoch with restored puppets. Staying put without
an intercepted native request fails. The fixture records both starting
positions and captures the client immediately after its walking pulse, before
waiting for the blocked request, so a missed doorway leaves visual evidence.
It derives the destination from the first new blocked request, then gives
the untouched host the same 4-second backward pulse without an intervening
warp. The host must reach that world/room in a newer completed-load epoch;
all clients must arrive, acknowledge and restore their puppets. The successful
host-exit checkpoint captures every instance. It never substitutes a client
CLI warp for walking. The passing run aligned client X from -101.878 to
-25.278 in two pulses, then intercepted 05/00 with unresolved programs
0xFFFF. All instances stayed at [5,6,0,1,1,0], epoch 3, after that client
attempt, with zero settled puppet error. The host's held walking pulse crossed
the doorway three times (05/00 → 05/06 → 05/00); both clients acknowledged
epochs 4, 5 and 6. The final checkpoint had [5,0,0,0,1,5] on all three games,
and maximum settled puppet error 14.14 units. Intermediate native-load puppet
positions were not sampled by that checkpoint. A natural same-room script
reload remains separate.
The first walking attempt (20261002-144249_net_host_transitions_native_exit_1)
missed the castle doorway: client start (-450.7,-260,1950) reached the wall
at (-535.8,-260,2045) without a native request. The captures show the
lateral offset; the host started near center at (48.5,-260,1950). This is
why alignment checks measured progress rather than assuming every client's
spawn remains centered. The subsequent passing run and its per-pulse samples
are retained in the table above.
The transition route evidence above covers transition and puppet behavior; it does not establish enemy/progress hash acceptance. The applied-state publisher and the additional nonempty fixture below now provide that check, but require a passing live report from the current build. Cutscene hold/resume remains separate.
Progress and applied-state hash fixtures
Native story state through the impaired Mac relay
Run 20261005-040303_mac_relay_native_story_join_root_1 passed 82 steps in
204.1 seconds; independent review accepted the bounded result. Two real games
on this PC used the private Mac relay with 50 ms delay, 10 ms jitter and 2%
loss. The host naturally acquired 08/0C story/program state, returned control
in [8,12,50,0,0,22], and returned to GoA. A client that never entered 08/0C
received the latest complete 8108-byte snapshot before its native join load,
then retained the flag through a same-room reload. Exact shared ranges and
personal apply invariants matched; protected saves were unchanged. Connected
incremental event delivery, cutscene hold/skip and broader native side effects
are separate tests.
The root-owned scratch fixture/adapter, actual relay receipts, sealed inputs
and commands are in build/rig/vuh1497-native-progress-mac-relay-20261005-01/.
See result
and review.
The earlier weak-control calibration 033029 remains FAIL; the stronger,
delayed native pulse in 040303 demonstrated 191 units of displacement.
progress_chest_goa_probe: one instance boots, enters04/1A, captures the room and records all actors, native treasure records and flag 409. This is exploratory evidence, with no memory write or chest interaction.progress_chest_goa_native_open: native camera-aware approach and reaction press, capture/dismiss the obtained-map popup with native confirm, and wait for normal gameplay before checking settled/opened state. This calibration fixture requires an initially closed chest and performs no memory writes.net_progress_chest_goa: three-instance chest acceptance. It clears only flag409 in memory outside GoA, verifies closed native initialization, opens the host chest through movement/reaction input, checks client next-load application, late-join current state and subsequent reload persistence. Each client apply must log unchanged personal bytes around allowed writes.net_statehash_enemy_negative: separate negative fixture using verified actor+0x9B8bit0x4(native ApplyStatDelta HP lock) on one client enemy, followed by a native host kill. It requires a FAILED client death log, actual surviving positive HP, two distinct fresh hash frame sets and relay DesyncEnemies. It restores only the original bit by read-modify-write while address/object/status identity still matches, then reloads through the host and requires nonempty parity. No shared object descriptor is modified. Failure remains failure; it cannot pass from a fabricated packet or an attempted mutation alone. The current fixture explicitly targets object ID311. Its prior object309 run20261002-185625_net_statehash_enemy_negative_1correctly failed: client1 retained native HP153 after a failed mirrored death and produced relayfields=2, but client2 independently acquired an unmatched object309 actor at HP160. Unaffected-peer parity remains strict. The control restored flags1159 to1155; that useful negative observation does not turn the full failed run into acceptance. Targeting311 is an explicit fixture choice, recorded in control evidence, not a population filter in the hash checker. The three-peer311 attempt also failed in20261002-190415_net_statehash_enemy_negative_1: client2 again gained an independent unmatched309/HP160 while the controlled client1 target remained alive. Both failed three-peer runs and their strict checker remain intact.net_statehash_enemy_negative_isolatedboots exactly two peers (host and client1) to isolate the detector control for object311. The checker uses every instance in that run, requires two fresh actual native mismatches and relay enemy-desync, restores the guarded HP-lock bit and reloads to require nonempty parity. This is explicitly two-peer detector evidence; it neither replaces the three-peer positive hash proof nor resolves the independently observed third-peer spawn divergence. The isolated live run20261002-192119_net_statehash_enemy_negative_isolated_1failed overall (134.9s) at its post-reload transition checkpoint. Its earlier negative checkpoint passed: two full native observations (host/client frames3643/1228 and3703/1289) retained client netID5/object311 at HP153 while the host counterpart was dead, the native client death log reported FAILED153?153, and the relay reported peer1fields=2. Guarded restoration verified flags1155?1159?1155. Reload reached completed epoch2 and matching full locations, but both peers had an active logical puppet with native friend pointers[0,0], no clone candidates and only the local actor. The strict native-puppet check correctly failed; the later restored nonempty hash check was not reached. Thus detector and flag-restoration checkpoints are proven within a failed run, while native-puppet reload recovery and the separate three-peer extra-spawn divergence remain unresolved. The fixture and checks remain unchanged; this run is not represented as acceptance.net_statehash_nonempty: extends the established12/0Bbattle-program-1 two-wave fixture. Three peers must agree before and after host damage, with at least four living native enemies. It then toggles only client 1's in-memory GoA chest flag 409, requires a fresh relay notice with exactlyfields=4, restores the byte and requires fresh agreement again. The original wave scenario and known courtyard failure are preserved.
The chest reward popup freezes native gameplay and holds animation 0x99
(153); bit409 changes before it is dismissed. The fixtures capture the popup,
send bounded native confirmation pulses, then wait for unfrozen gameplay, a live field, menu
0xFF, native event state exe+0xB65210 == 0 and event context
exe+0x2A11478 == 0. The retained timer is diagnostic. This preserves the native safe-state gate.
Do not interpret the popup animation as the reloaded opened chest state.
The confirmation pulses are a bounded calibration action, not proof that
confirm is what closes the popup: offline native tracing indicates an
animation/resource-driven lifetime. Gate samples also record UI tick delta
at 0x717484 to distinguish stalled timing from input readiness.
Calibration 20261002-190126_progress_chest_goa_native_open_1
failed the strict safe-state wait even after the popup disappeared and normal
HUD/gameplay returned: frozen=0, inField=1, menu=255, but the sampled timer
retained90. This is evidence to investigate whether the timer represents
active playback or retained elapsed time; the fixture does not mask it.
Dismissal failures preserve native chest/actor state and gate samples even
while the gate rejects traversal. The runner now uses the bounded native event-state/context predicate above;
the single-instance calibration additionally leaves and reloads GoA to
require the native opened initialization motion 0x98. Full mirrored-chest
acceptance uses the verified calibration below.
Corrected-gate single-instance calibration passed in
20261002-191026_progress_chest_goa_native_open_1
(66.3s). During the popup, native event state3 and nonzero context blocked
traversal with timer45. After2.906s, state/context were both zero while the
timer retained90; native leave-and-return warps succeeded and GameBridge
reported no active cutscene. The natively opened host chest used animation
153 with flag409 set. After native room reload it initialized to animation
152, while the other two closed chests stayed151. The full fixture therefore
requires immediate host153 and reloaded client/late-join152, not one animation
value across both lifecycle phases. Native opened-bit proof comes from the
reaction input, and no memory write changed the event state or timer.
Three-instance chest acceptance passed in
20261002-191345_net_progress_chest_goa_1
(189.3s). Native host opening changed flag409 and progress version1 to2;
the immediate snapshot showed only the host chest byte changed, while both
client bytes stayed closed until their allowed application boundary. Client1
then reloaded GoA at completed epoch3 with opened initialization motion152.
Client2 joined at that existing epoch, applied version2 and also initialized
the chest open. A subsequent all-peer reload advanced epoch4 with the exact
six-field location [4,26,0,0,0,0] and opened motion152 on all three peers.
Each transition checkpoint also retained relay acknowledgements and native
puppet checks; captures show next-load, late-join and final reload states.
Client1 applied versions1,2,2,2 and client2 applied2,2. Every immediate
around-write personal comparison reported before/after hash 124AA108 and
personal_unchanged=1. Full endpoint snapshots independently showed zero
changed bytes in characters, inventory, munny and EXP on all three peers.
The shared chest difference was exactly SAVE+0x23DF, byte0 to2, on each peer;
client2 also recorded a visited-room difference. All four on-disk
save hashes were unchanged. This proves native chest progress, boundary
application, late-join freshness and personal exclusions for this fixture;
it does not assert that every native story-setter side effect is mirrored.
The hash checkpoint starts a fresh observation window when called; old
matching logs cannot satisfy it. Every hash must follow the latest native
arrival, match its completed epoch and all six current NOW fields, and
include the complete raw row set associated by epoch/frame. It recomputes
KHE1 FNV-1a from the sorted positive-HP (netId, objectId, hp) records and
compares the result with the published hash. Positive checks require zero
unmatched rows and identical populations across all three peers. Dead rows
remain in the artifact, and negative controls retain unmatched rows rather
than filtering them out. An empty room cannot satisfy the default minimum.
progress_snapshot records actual bytes and SHA-256 for programs
SAVE+0x10/0x1C80, story +0x1C90/0x260, visited +0x22F8/0x98, chests
+0x23AC/0x34, and these complete personal exclusions: characters
+0x24F0/0xE04, inventory +0x3580/0x140, munny +0x2440/4, EXP
+0x36E0/4. A comparison reports each differing SAVE offset, old byte and
new byte. Native room initialization may legitimately change personal
state; these snapshots do not assert whole-range equality across loads.
The narrower apply invariant comes from [progresssync] apply logs:
personal_before, personal_after, and personal_unchanged=1 compare
personal bytes immediately around only the allowed writes, before native
room initialization.
Live probe on 2026-10-02:
20261002-182909_progress_chest_goa_probe_1
passed. Installed native F_EX040_HB (object 2540) resolves to treasure
585, item 592, room index 100, event 0x101E, overall flag 409. Its closed
motion is 0x97, at (0,460,900), and RVA 0x9ABC8F & 2 was zero.
The other two GoA records resolve to flags 410 and 411. This confirms those
installed rows; broader chest-range coverage still relies on the bounded
native/table evidence in build/rig/progress-ranges.md. Native host opening and mirrored open initialization are established by the
three-instance acceptance run below.
Applied-state hash acceptance passed live in
20261002-184220_net_statehash_nonempty_1
(180.2 s). All three peers had completed epoch 1 and exact location
[18,11,0,0,1,0], four native matched enemies (netIDs 3?6, object IDs
309/311), zero unmatched actors, and identical recomputed enemy hashes.
Native HP changed from 160 to 153 after host damage, with fresh agreement.
Toggling client1's actual flag409 byte from 0 to 2 after applied version 1
changed only its progress hash and produced relay fields=4; restoring 0
restored three-peer hash agreement. The full before/after snapshots had
zero changed bytes in every captured region on every peer, and all four
save-file hashes stayed unchanged. This run proves the hash and reversible
progress-control behavior; its later second_wave observation was empty
and is not additional two-wave-spawn evidence.
Native enemy census diagnostic
The strict 20-load acceptance failed at hash_transition_08 in
20261002-193320_net_host_transitions_statehash_acceptance_1:
completed epoch 10, location [5,6,0,1,1,0], published host count 0 versus
five unmatched object302/HP20 rows on each client, with equal progress hashes.
Those published counts came from the update-hook census; they were not an
independent complete native-list observation.
net_enemy_census_transition08.json retains that route through transition08,
then collects an immediate snapshot and two more after 1.5-second intervals
before retaining the original strict hash check. native_enemy_census reads
peers concurrently, with a 30-second bound per peer/sample. Its artifact always
retains errors and partial observations. Successful collection proves neither
peer parity nor enemy absence by itself.
Acceptance wrappers that compare native checkpoints must retain read-time freshness separately from stable identity. Require each peer's next read to start strictly after its prior read finished, carry those floors across checkpoints and explicit reloads, and retain publication-frame floors within one epoch. Two copies of one retained census are not independent samples. Original actor/netID membership also needs distinct native controller/record pointer and index associations; unique netIDs alone do not establish four records. The populated join recheck retains the replay and record-alias counterexamples and their corrections.
The exact-host-pack populated join variant 005739 correctly refused a pack
that changed from four309 to four309 plus two311 during qualification. Each
native round agreed with its own publication; no reference froze and no late
client runtime/join/reload ran. The one attempt is consumed and remains FAIL.
The next timing-only candidate adds one fixed settle before the unchanged
freshness/record/HP qualification, with no candidate recapture or retry. See
the closed result.
The settle-only 33-step candidate 012035 used a 7144ms fixed no-input wait,
then failed the unchanged death-history guard before freeze or late runtime.
Original IDs 1/2 left the current list with last HP160 and were reported as
despawned; no native HP-zero kill is inferred. No join/reload proof or retry.
See the closed settle FAIL.
Wave control 004941 passed with the reviewed single-call XYZ CLI. Full run
010014 then failed at step 45 after two issued kills,3/4, on the canonical
living-set equality before ID5. The failing canonical reply was not retained;
the next candidate must save that exact reply before checking it, preserving
every predicate and requiring a fresh control for its changed signature.
Both runs are closed, saves unchanged; the complete two-wave gate remains
unpassed. See the full result.
The new logging signature's control 012309 failed exact XYZ readback at
step 45 before kills or later region/enemy qualification. Its one control is
consumed. 004941 cannot be reused and no full attempt follows. The fixture
keeps exact equality; the next method qualifies another two-pack room. All
owned processes/relay closed and saves stayed unchanged. See the control FAIL.
Passive room collection 015317 remains FAIL. BC05/04's exact five installed
stage0 records qualified in fresh independent samples at0/3/6/9/12 seconds,
with enemy HP20 for four object302 and HP98 for one object303. At the terminal
15-second read, native safe gameplay failed after a logged hit killed Sora
(frame1399, HP24 to0). The collection correctly aborted before05/06. There
was no protect step, memory writer, kill, rescue or recapture. This is partial
host fixture evidence, not complete stability, populated cutscene, join or
second-wave acceptance. Four saves and owned PC/Mac closure passed. The advisor
reviewed one new protected BB04 no-kill control and a full five-then-three run
only if it passes, with every original check retained. See the passive result.
The protected control 021001 failed its final15-second fresh bit8 check.
All six samples kept the exact five-enemy pack and HP; Sora was alive/team0
but outside the selected region. Its one control is consumed, with no full or
retry. The source audit separates the diagnostic hit entry from ordinary
region-gated emission; displacement cause remains unproved. Under the advisor's
explicit goal clarification, a new fixture will require fresh native bit8 at
emission points while retaining every acceptance assertion, safety/read guard
and budget. It needs a fresh control; no old FAIL becomes PASS. See the
closed protected FAIL
and audit.
Fresh emission-scoped control 023926 passed: all six samples retained the
original five enemies and HP, with bit8 required at admission and the later
false value retained. Full 025347 remains FAIL before the third damage call.
The first two host applications were98->91 and20->13; the exact whole-phase
checkpoint, deaths and second pack were not reached. Actual entry weights
were8/15, unchanged. Its two-round reader refused one raw flags9B8 change,
0x4C83 -> 0x483 (xor0x4800), while the other returned fields agreed.
This does not establish the flag cause or final all-peer damage convergence.
Independent review accepted the closed FAIL; saves, execution pins and owned
PC/Mac closure passed. The advisor approved a new fixture comparison using
source-backed native eligibility bits, retaining raw words and every acceptance
assertion, followed by one fresh control and one full only after PASS. Old
FAILs remain. See the control PASS,
full FAIL
and independent review.
Fresh semantic-mask control 030712 passed. Full 030933 remains FAIL,
although all five first-wave enemies reached exact all-peer damage HP and
exactly-once deaths on both clients. It stopped at step47 before proving an
empty gap: consecutive native reads shared the coarse time.monotonic()
value, and the strict ordering guard refused. The second pack was unobserved;
the initialized step status does not prove completion. Closed independent
review adopted that scope. The advisor approved a new fixture that brackets
each actual native read with perf_counter_ns() and requires strictly ordered,
non-overlapping reads, retaining the coarse fields and every acceptance check.
One fresh control precedes one full run. No pauses or budgets were added.
See the control PASS,
full FAIL
and independent review.
The fresh QPC no-kill control 032130 passed and its closed evidence was
requalified by the actual full launcher. Full 032656 remains FAIL at initial
count convergence, before strict admission or any hit: the saved counts were
empty in all three games, and retained host publications stayed empty. All
three peers were verified. The final sampled host point was about20 units
short of the retained activation cylinder; why movement differed is unproved.
No QPC emission read or wave acceptance was exercised. Execution pins, four saves,
six owned PC closures and the closed Mac relay receipts passed. No retry.
See the fresh control
and full FAIL.
The independent review
adopted that scope. The advisor approved a new fixed fourth900ms activation
round with position logging after every round, a fresh control and one full
after PASS. A further activation miss calls for a capped native-activation
stop, not another fixed round. All original acceptance checks remain.
Fresh four-round control 034300 passed with four actual XYZ logs and the
same five enemies/HP in all six samples. Full 034523 activated the pack and
passed exact all-peer damage HP91/13/13/13/13. It remains FAIL after lethal
calls for IDs1/2, before the third call: one client native census changed during
its read, so the reader refused complete absence evidence. All four round-end
positions are retained for all three games. Whole first-wave death, empty gap,
second pack and final quiet acceptance remain unproved. All197 execution pins,
four saves and16 foreign files are unchanged; six owned PC processes and the
Mac relay are closed. No retry. See the
control
and full FAIL.
The independent review
adopted this scope: both issued deaths applied once per client. The sole
changing field was nextHandle on an original client actor already issued
lethal. Its mutation cause and successor identity remain unproved. The advisor
approved a new narrow adapter that retains such incomplete reads and polls a
fresh whole census within the original20s/48-round bounds, before any new hit.
Only that field on an already-issued-death actor is eligible; every other
changing field or failed guard still refuses. Discard counts must be reported
per phase. Complete native acceptance remains mandatory, with a fresh control
and one full only after PASS. No hit is reissued and no deadline is renewed.
Fresh linkpoll control 040051 passed and the full consumer requalified its
closed evidence. Full 040547 passed original all-peer admission and exact
damage HP91/13/13/13/13, then issued three lethal calls for IDs1/2/3. Each
reached hostHP0 and applied once on each client. It remains FAIL before the
fourth call: complete census03 and its canonical living reply retained two
survivors, but the first weight snapshot found a changed nextHandle on living
netId5/record55. Core identity pointers matched. This comparison was against
the fresh census, not admission; mutation cause and successor are unproved.
Census discards were zero in both reached phases, so the narrow classifier
was not exercised live. Whole-wave deaths, empty gap, second pack and final
quiet acceptance remain unproved. Saves, foreign files and owned PC/Mac closure
passed; no unchanged retry. See the
control,
full FAIL
and independent review.
The advisor approved a new fixture that retains raw nextHandle but excludes it from identity equality between the census and weight read, and between two independent weight snapshots. Core pointers, native weights, eligibility, HP, list bounds and all remaining guards stay. Within-walk list consistency and its existing narrowly bounded whole-census re-read remain mandatory. One fresh matching control precedes one full only after PASS. All acceptance assertions and recorded FAILs remain unchanged.
Fresh control 042344 and full 042645 now PASS the declared BB04
five-then-three gate through the private Mac relay. All77 steps completed on
three real KH2 instances on the Windows PC, with50ms delay,10ms jitter and2%
configured loss confirmed in each runtime log. Original IDs1-5 matched on all
three games at HP98/20/20/20/20, then91/13/13/13/13. Second-wave IDs6-8 matched
at HP98 each, then91 each, with original native bindings and no extras.
All eight lethal calls reached hostHP0 and applied exactly once per client.
The living-empty gap was observed before actual second-pack admission. After
the6.031s no-refill window, two fresh all-peer empty hashes and two complete
native censuses had zero living and zero native enemy rows. Final weight,
source-backed eligibility, fresh bit8 and QPC emission checks passed.
Independent review ADOPT and179 reviewed hashes matched. Four-phase census discards were zero; the narrow re-read branch remains unexercised. Raw links changed between separate censuses, but neither omitted comparison encountered a mismatch here. Full198 execution pins/91 historical sources matched, saves and16 foreign files remained unchanged, and all owned PC/Mac resources closed with25 fetched hashes matching. Every earlier FAIL stays. See the accepted result, fresh control and independent review. This qualifies the automated same-PC rig gate. Separate-PC friend play and reconnect remain outside this evidence; retained relay divergence captures also prevent an uninterrupted-equality or desync-free claim.
The HUD names live preparation
is accepted offline:32 steps, exact matched private AvatarBridge-v3 products,
literal admitted peer IDs and eight canonical capture attestations. Collection
PASS alone is not names acceptance. Root must inspect actual names/owner rows,
departure/expiry/reset, unchanged HP and labels; missing transient exposure
fails. The matched private v3 collection 020047 now remains FAIL: seven
actual name/owner/HP, movement, Waiting/Open and expiry captures were inspected,
but natural host exit0 was unqualified after the150-second reset deadline and
the reset capture is missing. The reset step's initial pass label is not proof
of completion. Runtime iteration count does not establish a wall-clock exit;
the final poll value was not retained. No eight-image or whole names acceptance
is claimed. Saves and owned PC/Mac closure passed. See the live result
and bounded shutdown audit.
The helper treats entities as candidates only. Checked reads validate native
HEAD/TAIL, every next handle and its region-table resolution, terminal handle0,
object descriptors including the F_ prefix, status and actual HP, and stable
identity/location/arrival/lifecycle bookends during safe gameplay. Cycles, cap
hits, unreadable pointers and changing loads remain incomplete. Hash coverage
comparisons require the latest post-arrival hash to match epoch/full location
and complete raw records/recomputed canonical hash; unavailable comparisons
retain raw logs without a coverage conclusion.
Actor identity rechecks now retain actorIdentityRecheck: complete before/after
field samples, each changed actor address, field name/address and old/new value,
and unread fields if a later read fails. The checked fields remain next handle,
object descriptor, status pointer, controller at actor+0x9E8, and spawn record
at actor+0x9F0. A partial or changed recheck still fails completeness; its
provisional enemy count is not absence proof. The existing per-peer deadline
also bounds this recheck. This improves later failure artifacts; it cannot
recover the missing changed-field detail from the earlier transition03 run.
The cache probe accepts only four inline buckets rooted at RVA 2AE5E60,
reads all 256 u16 record IDs twice, and brackets active pointer/counter reads.
Record IDs are not object IDs. Controller/record shape checks and bounded
ordinary controller-table observations are separate causal evidence: script
entries are skipped, table cap64 is diagnostic rather than a proven native
limit, and incomplete provenance does not erase complete list evidence.
Controller stage/cooldown, tracked activation/player positions, raw spawn
records and table/key membership help distinguish activation history. These
observations remain read-only and do not establish every producer's semantics.
The canonical controller/lookup ABI reference records checked field widths, fingerprints and supported record scope.
The follow-up geometry capture walks actual linked regions for ordinary type2
controllers, prioritizing header30. It reads each rooted 64-byte descriptor and
runtime inverse matrix/extents, accepts only the verified BOX/CYLINDER/INFINITY
vtables, and rechecks links, geometry bytes, controller roots, full location and
lifecycle. Descriptor cap64 is an explicit diagnostic limit. The native
activation float4 follows accessor 3B5B40: a checked nonnull parent-handle
resolution selects actor+0x70; a zero handle selects +0x670. Unknown or failed
resolution cannot silently select the latter.
geometryComparisons evaluates every sampled peer activation point against the
captured regions with float32 operations, marks near-edge or nonfinite outcomes
uncertain, and preserves whether the point moved during capture. Concurrent
reads are not atomic frame observations or native predicate return values.
New causeContext.positionReadback and positionSelectorReadback receipts
retain exact little-endian before/after bytes, requested field addresses,
partial reads, unread fields and byte differences from the four existing
reads. No extra read, retry, tolerance or acceptance change was introduced.
pointStableDuringCapture remains the exact aggregate comparison over both
activationActor and playerActor; movement in either can fail it. Role labels
that alias one address share the CLI's address-keyed returned value, without
independent timestamps. Empty/incomplete receipt coverage cannot certify a
stable point. Ten actual-collector controls with synthetic memory passed;
historical reports lacking repeated bytes remain unchanged and unexplained.
geometryComplete and causal limits are separate from primary census
completeness; a successful list collection cannot prove incomplete geometry.
No native functions are invoked and no game memory is written. The transition08
run below predates region capture; the later partial transition03 result records
the live geometry evidence and its limits.
Live collection succeeded in
20261002-200856_net_enemy_census_transition08_1,
but the retained strict parity check made the overall scenario FAIL
(208.9 s). All nine peer snapshots were complete with valid hash comparisons.
The first snapshot had no native enemies and empty caches everywhere; the
later two had zero host enemies and five actual client enemies, matching each
peer's published rows. Type-2 controller header30 had flags2 on the host and
flags10 on clients; client caches gained record IDs 11,12,13,14,18 while the
host cache stayed empty, and activation positions diverged. This establishes
native spawn divergence in this reproduction, not a hidden host update-hook
population. Initially equal caches do not support retained cache history alone
as its cause. See native findings and inference limits.
All four disk saves remained unchanged.
With the newer native-census DLL, the unchanged transition08 diagnostic
failed earlier in
20261002-204129_net_enemy_census_transition08_1
(176.7 s), at retained hash_transition_03, before geometry collection. Epoch5
location [5,6,0,1,1,0] had zero host rows versus five unmatched object302/HP20
rows on each client, equal progress hashes, and relay enemy-desync reports for
both clients. No geometry artifact was produced; all four saves were unchanged.
This does not replace or reinterpret the earlier transition08 observations.
net_enemy_census_transition03.json is a separate diagnostic retaining the
existing transition08 fixture's exact prefix through transition_03. It inserts
three native census/geometry snapshots before the unchanged strict hash03
check and assertion, then ends. Prior parity checks remain strict, and the
original transition08 fixture remains unchanged. This collects evidence at the
earlier failure boundary without relaxing acceptance.
The geometry03 run
20261002-204742_net_enemy_census_transition03_1
was FAIL (164.6 s): snapshot0/client1 failed the actor-identity stability
recheck, so its provisional zero-enemy count cannot establish absence. Native
headers, completed epoch, gameplay/lifecycle and cache were stable; the artifact
does not identify which actor field changed. Causal reads for that sample were
skipped, and the trailing strict parity check never ran.
The preserved geometry artifact contains eight valid native/causal snapshots out of nine. Snapshots1/2 include complete seven-BOX geometry for header30 on every peer. Across every peer's captured geometry, the host's sampled activation point was outside all seven regions and both clients' points were inside descriptor indices5/6, without edge uncertainty. All seven source descriptors and inverse-matrix/extent bytes matched across the eight valid captures. These are offline containment results for sampled native float4 points, not instrumented native predicate returns. All four saves were unchanged. The compact extraction receipt retains the missing comparisons, key regions and byte fingerprints. Partial evidence does not make this collection green, replace earlier failures, or validate an activation-authority intervention.
The full unchanged-route acceptance subsequently passed (246.9 s) in
20261002-205416_net_host_transitions_statehash_acceptance_1,
using DLL SHA-256 62296DA088BDD4AC9023399A1F6FF763091FF7983D245983E2501E1BC54EDCA0.
Removing only the added hash checks/assertions reproduces all 60 original
acceptance steps exactly. The audit confirmed 20 native loads after late join,
including the identical-location final reload, 23 arrival/binding checkpoints,
130 actual native puppet bindings (maximum sampled pose error 43.472 units), and
23 hash checkpoints containing 134 fresh peer samples with matching full
location/epoch and enemy/progress hashes.
Every accepted enemy population was empty, as were all 324 published hash
headers in the logs. This route pass does not prove nonempty native spawn application. At the previously failing 05/06 boundary,
logs show host header30 capture, both clients accepting leases and applying the
host point, with explicit qualified-controller holds between available leases.
The settled point (54.072,-260,1950,1) appears in host capture, both client
leases and native apply logs. These logs round to three decimals and are rate
limited; they do not establish byte identity for every response. All logged
cumulative qualification-unavailable counters stayed zero; unsupported native
controllers remained separately visible. Four disk saves were unchanged.
The independent audit receipt
retains the route, serials, native binding/hash counts and scoped hook evidence.
This pass preserves the earlier failures and geometry limits; it is not a claim
of general authoritative spawning or renewed nonempty combat coverage.
The focused connected-client expiry control also passed (93.5 s):
20261002-210128_net_activation_lease_menu_1.
Native host Start input made the combined menu/pause safety predicate unsafe
and stopped usable host samples while both runtimes remained connected and the
client stayed in safe gameplay. The artifact does not retain which gate changed
or prove a visibly open menu. After a wait exceeding one second, repeated
qualified header30 Hold observations increased the
hold counter from 505 to 749 while Apply stayed 388 and no new lease arrived.
A second native Start input restored safe gameplay, source 836 after baseline
source 752, and Apply 389.
Both peers retained epoch 1/full location [5,6,0,1,1,0], and all four saves were
unchanged. This proves unsafe-gate source cessation and connected-client Hold/resume for
the qualified controller. The exact 500 ms deadline is established by portable
lease tests, not direct live deadline telemetry. It does not prove
disconnect/reconnect, nonempty spawn application,
missed short host crossings, or cache/stage/refill authority.
The separate ignored party-availability probe
20261002-194936_net_party_availability_probe_1
passed its diagnostic checks: both native friend pointers were already zero on
first native 12/0B entry before networking and remained zero after epoch1/2;
world18 party word stayed 0x12121200 and all four disk saves were unchanged.
This narrows the earlier isolated negative-control recovery failure; it does
not turn that failed recovery into a pass or establish a network regression.
Checked native-list coverage regressions
The checked native-list coverage change passed the Release DLL build and the native-wave regression (153.6 s), nonempty hash/progress control (166.1 s), and focused native-death detector (102.4 s). All four disk saves were unchanged in both suites. The wave fixture recorded two real second-wave enemies and six native client deaths; the nonempty fixture's later empty-wave sample is not extra wave evidence.
net_native_census_death_control.json preserves the isolated negative fixture
through its failed native lethal/detection/guarded bit-restoration checkpoint
and ends there. It verifies the changed lethal/census path independently of
the known unsupported battle-room native-puppet reload. It does not close
that original recovery failure or the courtyard spawn-authority gap.
The newer activation-lease DLL subsequently failed the unchanged wave fixture
in 20261002-210512_net_enemy_sync_waves_1
(155.5 s). Before damage, the host had four object309 actors, while each client
had two matched object309 actors and two unmatched object311 actors. Equal
counts passed the original count-only readiness check despite this divergence.
All four selected host actors received native seven-point damage; an alive
host despawn/refill then introduced a fresh HP160 object309 actor before the
saved HP sample. The surviving matched client actors had HP153. The original
strict HP assertion correctly failed. The
archived audit preserves
the sorted populations, native damage receipts, refill and unmatched rows.
This is a real population/lifecycle regression, not an actor-list ordering
artifact or evidence that a matched actor's HP update was lost.
net_enemy_census_waves_provenance.json is a strict provenance diagnostic.
Removing its inserted readiness wait and three native_enemy_census steps reproduces all 44
original wave steps exactly, including all nine 900 ms native movement pulses,
count-only readiness, host damage, and the original strict HP assertion. The
insertions capture one snapshot before the first movement pulse, three after
the original four-second settle and count readiness, and one after the saved
damage sample but immediately before its strict assertion. Each capture
includes checked native actors, controller/record metadata, rooted cache and
ordinary controller/table evidence where available. It retains extras and
unmatched rows. A repeated divergence must still fail; no expected-failure
exception changes the result. Read windows change timing, and incomplete
collection can fail before the original assertion. Neither settled timing nor
equal counts establishes identity parity.
The first native diagnostic attempt,
20261002-234801_net_enemy_census_waves_provenance_1,
failed after 131.0 s at its first census, before movement or damage. Peer 2 was
still in its runtime-triggered load: native head/tail and inField were zero,
and its latest log was a client-issued transition. Its later copied log
records load completion, arrival and hashes. Peers 0 and 1 had complete stable
censuses. This is incomplete startup evidence, not a combat regression or proof
of native enemy absence. The fixture now waits up to 30 s for current completed
arrival, safe gameplay flags, valid native roots and actor candidates on every
peer before the unchanged census. It does not retry or discard census failures.
The trace audit confirmed all selected spawn and lifecycle probes installed on all three PIDs, and structurally complete recorded envelopes at their summary cutoffs, with no recorded drops or native faults. Provenance remained incomplete: 23 fixed wrapper returns and 36 disposal events were noncombat or had unavailable early role/stamps/poststate. No object-309/311 production or combat removal was recorded. This does not identify the prior strict wave failure's cause.
The revised diagnostic,
20261002-235825_net_enemy_census_waves_provenance_1,
passed readiness and the first census, then all nine original movement pulses,
the original four-second settle and count readiness. It failed the second
census after 151.7 s, before host damage or the original strict HP assertion.
All three host samples reported Python PermissionError: [WinError 5] Access is denied during descriptor acquisition. Traversal had reached seven native
nodes and terminal handle zero, but host classification and after-rechecks
were unavailable. This is not an observed timer/identity change. All six
client samples were complete and stable, with two object-309 and two object-311
enemies at 160 HP. The
collection audit retains all
invalid observations and reconstructs the pending descriptor command without
running it against retired pointers. A future capture needs operation/argv and
traceback evidence before attributing the access-denied cause. All four save
hashes were unchanged, with no save violations.
The revised trace audit
again confirms all selected probes installed, structurally complete envelopes
at recorded cutoffs and no recorded drops/native faults. Each peer recorded
four requested object-309 entries (records 14–17) and two object-311 entries
(records 34–35) under header 115, type 2, six records, through callsite 3FE83F
within a native tick/dispatcher. Initial nonzero return values lacked checked
status metadata; these do not establish creation incarnations. Checked alive
309 removals erased cache records 17 then 16 through 3FFD90 caller 41181D,
followed by disposal through 3B45C0 caller 3BFD87. HP stayed 160 and the
controller count stayed six. Subsequent requests for those records returned
zero repeatedly (654 host, 660 per client). No lethal/count-decrement events
were recorded for these removals. Their causes and the zero-return predicates
remain unresolved; the earlier strict native wave failure is still open.
Bounded static follow-up against the saved Steam Global PE explains several
observed phases without selecting their live cause. A nonzero ordinary fixed
return is a constructed actor whose base constructor zeroes status; separate
411350/4112B0 operations assign stats later. Alive mode-2 removal erases its
cache ID without decrementing controller count or changing stage. 411800
calls removal bookkeeping then tail-jumps disposal, preserving outer return
3BFD87; those two observations do not prove separate triggers. Ordinary
type-4 factory zero returns can follow its weight admission check
(objentry+54 <= float[2A0F7DC] - float[2A0F830]) or allocation failure.
The byte-backed static contract
records exact RVAs, setup/removal predicates and limits. Live predicate
operands, allocation outcome and creation incarnation remain unobserved.
The collector now records up to eight batched-read failures per peer, including stage, command argv, phase, completed field count, exception type, WinError, filename and bounded traceback. It preserves the original exception and incomplete observation without retries. The offline error controls cover real local PermissionError/WinError 5, a child-reported tool error and a malformed response; geometry 33, fixture validation 37 and auditor 37 controls also passed. This improves diagnosis; it does not fix or explain the native access-denied event. Probe/entity/log reads outside the batched reader retain their earlier error reporting.
The next run,
20261003-001955_net_enemy_census_waves_provenance_1,
passed all 48 steps in 170.6 s with the same DLL. All three census windows
were complete. The settled native pack contained two object-309 and two
object-311 enemies on every peer, with zero unmatched bindings and three fresh
pre-damage samples per peer. Host-only damage reduced all four enemies
from 160 to 153 HP everywhere, satisfying the unchanged strict HP assertion.
The subsequent host lethal call produced four native death applications on
each client. All four real-save hashes stayed unchanged, with no violations.
This is bounded first-pack HP/death evidence. The captured second wave was
[[], [], []], its lethal call hit zero actors, and the final battle-state
check was zero everywhere. It does not prove a second wave occurred or that a
barrier lifted. The trace audit
recorded four death-bookkeeping calls per peer, each enclosing one count
decrement: the controller count moved 6 to 2, while stage stayed six.
Earlier alive removals had not decremented it. The installed probes recorded
no drops or native faults, but creation status and some caller metadata remain
unavailable, so provenance is incomplete. This pass neither explains the
retained earlier divergence nor diagnoses the intermittent access-denied
error, which did not recur. The post-damage census has one sample per peer;
eight retained geometry captures had moving player positions, so structural
geometry completeness does not certify stable positional predicates. The
census audit and
damage audit retain these
limits. Six host death announcements include two earlier alive-despawn policy
events, rather than six native kills. General wave, barrier and boss gates
remain open.
For a future run in the desktop live lane, scope creation tracing to the runner process and its children; do not change global configuration:
python -B -c "import os,subprocess,sys; raise SystemExit(subprocess.call([sys.executable,'-B','tools/scenario/run.py','tools/scenario/scenarios/net_enemy_census_waves_provenance.json'],env={**os.environ,'KH2COOP_SPAWN_TRACE':'1'}))"
The setting is inherited by all three kh2ctl launches and accepted only as
the exact string 1 when the DLL installs. The first attempt is recorded above.
The fixture does not assert trace readiness. Inspect every PID log for all four
spawn hook availability bits (fixed/generated/dispatcher/script) and lifecycle
installedMask=31, as well as unsupported callers, unavailable reads, interrupts
and drops. Per-event record/state lines and all eight cache chunks per available
phase must be present. Wrapper snapshots precede caller-owned bookkeeping;
enclosing tick completion is distinct evidence. Script TLS records dynamic
enclosure, even when a native tail jump leaves the dispatcher with an unavailable
DLL caller address. roleAvailable=0 retains raw observations without a valid
role/serial stamp. Current bindingEpoch/netId correlation is not a creation
identity. Count-decrement events have explicit zero actor placeholders.
Audit saved PID logs offline, retaining the JSON report beside the run:
python -B tools/scenario/trace_audit.py path/to/host.log path/to/client1.log path/to/client2.log --output build/rig/native_trace_audit.json
Exit 0 means the supplied recorded envelope and counter cutoff have no identified coverage/structure gaps; it is not KH2 acceptance or proof every native producer was observed. Exit 1 preserves incomplete provenance; exit 2 is usage/file I/O failure. The report separates structural completeness from provenance completeness and retains unknowns, dropped/interrupted events, partial hook installation, cache chunk omissions, unknown ordinary record indices, missing lifecycle record/classification provenance, unavailable generated positions and summary-tail coverage. All PID logs and partial census artifacts survive failed runs. Bounded queues, missing shutdown drain and unhooked paths remain limits even for a passing audit.
The Windows-only headless trace tests are separate from gameplay:
cmake --build build --config Release --target kh2coop_spawntrace_test kh2coop_lifecycletrace_test
./build/Release/kh2coop_spawntrace_test.exe
./build/Release/kh2coop_lifecycletrace_test.exe
python -B -m unittest discover -s tests -p test_trace_audit.py
Spawn/lifecycle tests passed 38/24 checks under /W4 /WX and Windows
AddressSanitizer using synthetic originals and test-owned memory. They cover
original-call preservation, exception propagation, scope recovery, queue loss
and foreign-thread affinity, not installed hooks or live provenance. The auditor
passed 37 controls and rejects the archived untraced wave logs. Fresh build/test
scope is recorded in the
expanded offline receipt.
net_activation_native_nonempty.json is a separate 78-step courtyard control
at full location [5,6,0,1,1,0]. Its first native attempt is an overall failure
with passing movement, negative activation and populated-pack checkpoints.
Clients enter the measured header30 BOX geometry by native input while the
host stays outside; checked native census and fresh hashes must remain empty.
The host then enters and all peers must expose exactly five bound living
object302 actors, with independent native-list and geometry evidence. Native
host damage must reduce each of the same five netIDs by exactly three HP,
with matching client populations. Native death logs must verify HP crossing
to zero for every selected netID on both clients, followed by empty hashes in
the same epoch/location. No transform writes supply movement. Each actor's
250 ms direction probe must pass before a computed 100..2000 ms input pulse;
uncertain movement fails without a fallback. Captured geometry is checked
with offline float32 containment, not native predicate instrumentation.
Offline checks passed for all 36 fixture structures, 33 mocked geometry, identity/deadline and provenance controls, and 12 fixture expression controls. The latter evaluated all 78 steps against synthetic state and archived geometry, matched all four regexes against archived native logs, rejected replacement netIDs/unchanged HP/extras/empty positive populations/unstable activation points, and confirmed that the unchanged wave assertion still rejects the archived failure. Receipts: native reader controls and fixture controls. These checks establish tooling behavior only. They do not calibrate native movement, establish a live nonempty pass, or prove general spawn authority.
The first live run
failed after 159.8 s at step 63. All three bounded movement probes and
computed pulses passed. Two complete stable captures put both clients safely
inside the measured BOX while the host remained outside all seven regions;
independent native censuses and two fresh matching hash samples remained
empty. After host entry, two fresh hash samples and two complete native censuses
showed exactly five matched object-302 Shadows at 20 HP on every peer, with
zero unmatched actors. However, the second positive geometry capture reported
pointStableDuringCapture=false for the host. All sampled containment results
were safely inside; this failed positional stability, rather than demonstrating
an outside host. The assertion now names its combined stability/geometry gate
more accurately, with its expression unchanged. Host HP/death checks were not
reached. All four real-save hashes were unchanged; no save violations occurred.
Retain the unstable capture, the overall failure and the distinction between
sampled float32 geometry and native predicate execution.
net_enemy_sync_waves_acceptance.json strengthens the two-pack check in a new
fixture while preserving all 44 original steps and their order. It requires
the first pack to contain two object-309 and two object-311 enemies, then an
actual second pack of two object-309 enemies with fresh, unique current binding
IDs disjoint from the first. Both packs need two fresh matching native hash
samples, independent checked censuses, exactly seven HP of host damage and
positive-to-zero native death logs per ID on each client. Empty second packs
cannot pass. The offline receipt
records validation and 27 controls, including empty/wrong-type/reused/duplicate
IDs, changing bindings, wrong epoch/location and receipt-only death negatives.
Independent review demonstrated that comparisonValid alone did not establish
current native identity or HP: it validates the logged hash before mismatch
arrays are computed. The three strict census assertions now require both
mismatch arrays empty and unique bidirectional native address/objectId/HP
joins to the latest published living rows. Controls reject changed addresses,
types or HP even with a valid stale hash. IDs remain current bindings rather
than creation identities, and battle-byte/controller counts do not prove barriers.
Its first live run
failed in 152.0 s, before host damage, at the first independent census.
Two fresh matching native hash samples showed the expected first-pack bindings
and 160 HP. Four client census samples were complete and stable; both host
descriptor batches failed to start kh2ctl. The preserved tracebacks end at
Python _winapi.CreateProcess with WinError 5, rather than a native memory API.
Defender events 1116/1117 match that command line and report
Trojan:Win32/Commando.A!ml detection/removal. The
startup audit retains the
exact argv/tracebacks and detection evidence. The detection's correctness is
unestablished. Further live runs are held while offline observer work continues;
no exclusion, permission change or executable renaming was used. All four
real-save hashes stayed unchanged, with no save violations. The earlier
descriptor failure lacks this fresh correlation and remains separate evidence.
Scoped native decision observers: offline candidate
The opt-in trace now observes genuine admission 3A1F00 and type-4 allocation
152430 returns within each existing wrapper. Raw float bits, actual size/RAX,
scoped call counts and effective coverage distinguish executed admission
rejection from allocator null without replaying either helper. Budget snapshots
remain boundary samples. Missing, remapped, partial, repeated or interrupted
observations retain an unknown/ambiguous outcome; constructed status-zero actors
do not hide otherwise available raw decision facts.
Removal predicate 3DAC30 similarly records its genuine result plus scoped
3B4420 and 3CE550 child results. Full stable coverage and exact child counts
distinguish five normal branch outcomes; checked operands do not substitute for
executed returns. The predicate finishes before later removal/disposal, so it
does not enclose those later events. Existing lifecycle kinds keep their IDs;
removal-predicate is kind 5, with parent/script/auxiliary bits 32/64/128 and full
installation mask 255. Earlier mask-31 logs retain their original scope.
The allocator detour fills existing POD only, with no diagnostic allocation, logging, role callbacks or returned-pointer reads. Originals execute once; foreign callers pass through and native exceptions propagate after scalar TLS cleanup. Full byte/caller guards, independent availability and bounded loss counters remain explicit. Additional detour overhead and live relocation are unmeasured. Shutdown retains the existing requirement for a quiescent owner.
Windows Release and ASan builds passed; synthetic spawn 75, lifecycle 59 and actual-source native-hit 42 controls passed in both configurations, with no sanitizer findings. The saved-log auditor passed 59 controls (37 prior plus 22 predicate controls) and gives historical logs no new branch claims. Initial sanitizer launches lacked the installed MSVC runtime on PATH; adding its existing directory only to each test process environment resolved that dependency. No global configuration changed. Cross-reviews accepted both observers and the removal auditor. The new DLL has no live hook installation or gameplay validation; the preserved earlier native results used the prior DLL. See the predicate receipt.
Guarded native menu save (2026-10-04)
James authorized one save-menu attempt. In local Parlor run
20261004-150952_save_menu_guard_once_1 (PID 729136), root inspected the native
Save flow and sent one latched overwrite confirmation for existing slot 04.
The returned file list showed its time change from 5:55 to 5:59 and a changed thumbnail;
a separate completion-text dialog was not captured. Four post-confirm guard
redirects name the exact sandbox KHIIFM twin. It was absent before input, became
a nonempty 7,073,032-byte PNG-bearing container, and stayed stable in two fresh
post-cleanup samples. Its SHA256 is
0932731bba0e04bf17773ce5afc968579dd6d3351cb4e396ba29695fe55200b5, different
from the untouched original. All four original files, including Steam VDF,
matched historical/prelaunch/premenu/step-exit/post-cleanup hashes and file set.
The automation run remains FAIL, exit 1. The fixed 300s menu deadline expired before root submitted the finish command. The observed protected menu write is sealed separately; it does not reclassify that scenario or establish a full save-format audit. Earlier preflight 145847 remains HANG/FAIL with no save attempt or sandbox output. The one writing attempt is consumed; no retry, copy-back, restoration or deletion occurred.
The menu pauses the gameplay frame used by the ordinary watchdog. The reviewed
private supervisor used fresh, strictly advancing same-PID native capture
completions in an explicitly observed Save menu, retaining fixed 30s response and
300s total bounds. This changes no shared runner watchdog and is not a reusable
passing menu scenario. See the sealed result
and publication evidence.
The independent review
accepts this bounded write/protection observation and retains the automation FAIL.
Its raw nested step status says pass despite the overall failure and traceback;
that stale value does not establish completed finish evidence.
Native pause and event input callback (2026-10-05)
Set KH2COOP_INPUT_CALLBACK_TRACE=1 before launching an injected game to log
bounded, per-thread raw-input entry counts and checked event/menu fields. The
trace defaults off and changes no input or gameplay behavior. It records two
consecutive entry reads, availability and exception codes; these reads do not
bracket the original collector or establish world-mutation authority.
Native Pause run 043841 retained roughly60 callback entries/s in menu10 while
ordinary avatar publication stopped. Actual story-event run 044129 retained
roughly30 entries/s through event states2/3/4, returning to roughly60 after the
native event ended at [8,12,50,0,0,22]. Movement resumed after the event. Both
runs passed without crashes or protected-save changes. This identifies a
candidate observation/release callback; multiplayer cutscene hold and skip
remain unimplemented. See the probe result
and source review.
The optional trace also records independent atomic owner-frame and owner-thread
observations. Actual event run 050837 retained advancing list-head owner frames
through states2/3/4. These fields are not a coherent ownership token and do not
authorize world publication from the raw-input callback. See the
owner coverage result.
Connected native client hold (2026-10-05)
20261005-094229_mac_client_native_event_hold_entry_wait_1 passed all 55 steps
with two real games through the Mac relay at 50 ms latency/10ms jitter/2% loss.
Runner, games and client runtime used Normal priority. Both peers were admitted
in empty GoA before the host entered the native08/0C evt1 event. Authenticated
host Acquire, native input receipt and actual owner ACK preceded the client's
menu10 pause. A 250 ms nonzero mailbox command was consumed while raw input stayed
neutral and the client stayed stationary. Native captures and advancing input
callbacks kept the bounded paused watchdog qualified.
The host ended naturally at [8,12,50,0,0,22]. The client consumed the exact
successor Transition and Release, closed its owned menu within 406 ms, and kept
input neutral until native owner processing confirmed full arrival, complete
empty manifest/census and current masked progress equality. All8108 shared bytes
matched; story0->2 reached the connected client. Complete discarded the held
sample. A subsequent fresh 250 ms movement command moved the same actor 127.79 units.
No crash, personal-byte change or protected disk-save change occurred. Root
inspected the host event, client pause, final gameplay and fresh-movement images.
Both flags default off. In this development probe the host game had
KH2COOP_EVENT_HOLD_PRODUCER=1 and control0; the client game and its runtime had
KH2COOP_EVENT_HOLD_CONTROL=1 and producer0. The raw-input callback reads only
the separate scoped control channel and overlays normal Start/neutral input;
world delivery, arrival and progress servicing remain on the native owner path.
The local heartbeat limit was 1500 ms (4000 ms since VUH-2640) and opening/closing limits are 1000 ms.
Retirement, unsupported ownership, resync and overflow abort the one-shot interval.
Acceptance is one already-bound empty-room client and one natural event, with
sampled mailbox suppression. Physical-controller interference, populated rooms,
repeated holds, skip/vote, reconnect during a hold and uncertain-menu cleanup are
not covered. Earlier failed probes stay failed; 091838 independently passed
with an AboveNormal client runtime. 093030 failed a premature harness callback
pair check. The next-copy entry fix grants no paused-liveness credit while waiting
for its second callback; the ordinary watchdog remains active with a fixed 5 s
entry limit, deducted from the 120 s hold ceiling. The final result requires an
explicit native close-clock bound rather than treating Complete as a timer proof.
See the hold result, Normal-priority review and prior source review.
Known limits
bootopens the save list on the last-used slot. The list wraps through all 99 slots, so there is no absolute row to count from:slotMovesmoves relative to that slot. Saveguard keeps the last-used slot unchanged on PC1 (slot 4, BC Parlor, as of 2026-10-17), so"slotMoves": -1loads slot 3 (FM-02,boot_fm02.json). Assert the loaded room as well. The pixel checks assume 1920×1080.- The hang check needs the room to be live: it doesn't cover the title screen or a load that never finishes (those surface as step timeouts).
- A protected native menu write was observed once in the authorized attempt
above; its automation run failed the final evidence deadline. A reusable
passing menu scenario remains open. The menu runs as a scheduled task
(
0x1512B0); no direct native writer/task invocation was used. Standing save restrictions remain, and this single save authorization is consumed.
Route runs (VUH-2253)
net_route_tt_hb_3p.json is the milestone route run (VUH-2250): three
instances on an ENet loopback relay, host-led through TT and HB combat, a
host-skipped cutscene, the BC Thresholder boss and a friend's death.
python -B tools/scenario/run.py tools/scenario/scenarios/net_route_tt_hb_3p.json
Live KH2 must start in the desktop session (Session 1). Agent shells run in
Session 0, where D3D12 fails and the game dies about 3 s into boot
(0xC0000005, Render: D3D12 probe failed). Queue the command through the
desk bridge with tools/rig/desk-scenario.ps1 (the kh2-rig skill has the
details).
"playerDefaults": truegives everybootandruntimethe player package'sKH2COOP_policy (friend_package.child_environment): the shipped defaults, with parked features forced off. A step's ownenvstill wins. The run refuses if the runner's environment already hasKH2COOP_variables. The applied set is inreport.jsonunderplayerEnv. Automation keeps the quick Beginner revive (KH2COOP_REVIVE_POLICY=beginner, D17) unless the scenario sets"revivePolicy": "native", which runs the product rule for the save's difficulty.checkpointsteps split the route into segments. Before the first checkpoint, setup failures end the run as usual. After it, a failing step ends only its segment, and the run skips to the next checkpoint. Crashes and hangs still end the run.report.mdlists how far the route got, the furthest passing segment, and every failed segment in route order. The first one is the breaker to file.- Each segment checks that all three are in the host's room, that their enemy HP lists match, that friends can move, and that no runtime logged a new desync notice.
- Waves spawn only when a Sora enters their trigger, so combat segments teleport each Sora there first, as the T1 fixtures do, before the bots fight. A synthetic kill starts the Thresholder's native defeat (bonus, then a ~10 s host event). Its actor stays listed at 1 HP, so the boss segment waits for a new host event hold with the boss at 1 HP or less on all three, rather than for it to leave the list.
- After that event every instance stays on the white loading screen with the
heart. The solo control without co-op (
solo_boss_thresholder_defeat) does the same, so it comes from warping straight into the fight, not from co-op. The host's warp to GoA leaves it. Don't press Start there: it opens the pause, which stops gameplay frames (the runner reports a hang) and holds the warp at the safe-state gate (VUH-2303, run20261015-002627). - Soras are protected (team 0) during combat so the route can keep going. The last segment removes friend2's protection and deals a lethal native hit, to show what a death does under the shipped defaults.
- The HB rooms are the proven T1 wave rooms (
04/15is the Cavern of Remembrance), not first-visit HB. The route's own first-visit rooms and a door-entered boss are still to add.
Route T1 mirror smoke (VUH-1789)
net_route_t2_magic_phantom.json is the retained T2 casting smoke at HB04/15
btl26. It checks two-player Normal current/max HP and shared deaths, and
records host/friend spell clips for visual review. Only the owned local Soras
receive temporary RAM HP999 for survival; protected saves remain guarded.
It does not assert projectile timing, damage or remote-target equivalence.
net_route_t1_ordinary_waves.json visits HB Iron Hammer, TT Driller/Soldier,
HB Armored Knight and BC Hammer Frame on one two-player loopback. Each visit
requires living target families on both peers, matching native Normal-scaled
max HP, mirror driving, shared damage HP and death agreement. MIRROR,
POPULATION and TARGET_ALL are enabled. net_route_t1_hb_iron_hammer.json
is the shorter addition-only fixture. Clips show native waves and their deaths;
the report records numerical HP. The fixtures do not qualify remote-player
attacks beyond the existing native Sora-clone room scope.
Poll subprocesses and console cleanup
The canonical kh2ctl wrapper and bridge() avatar poll use
CREATE_NO_WINDOW, including when the runner has detached from a console during
G7 runtime shutdown. Capturing stdout/stderr alone does not suppress allocation
of a new console. On PC2, rev6/rev7 polling began creating orphan console hosts
immediately after that detach; those hosts were outside Windows jobs after their
poll processes had exited. Never clean them by name or timestamp alone.
subprocess.run reaps normal and timed-out poll children. Context.close() joins
the protector before exiting (its existing command timeouts bound an in-flight
poll), and waits after force-killing each retained owned helper. The offline
Windows control uses disposable Python helpers, checks both polling wrappers,
normal exit, timeout and in-flight protection cleanup, and counts retained owned
child/console processes:
python -B tools/scenario/console_process_control.py --out .local/console-control.json
It makes no game calls. Total process/console counts are recorded as observations; any retained owned poll child or console process fails the control. A new sealed fixture must repin the canonical runner hash after adopting this change.
VUH-1498 repeated host event hold and skip (2026-10-13)
20261013-062809_net_event_hold_and_skip_1 passes in 211.6 s with two owned loopback
games. Natural 08/0C evt1 and its repeated, natively skipped playback both hold
the client behind the automatic waiting overlay. A 250 ms client movement command
moves0 units while paused; fresh 350 ms commands after release move 159.5/175.3
units. Both cycles land at [8,12,50,0,0,22] with 8,108 shared masked progress
bytes equal. All four protected saves remain unchanged; owned games/runtimes
and relay close, port27796 is free. Four 3 s / 6 fps clips (72 total frames) were
reviewed. Evidence and the natural-event profile are retained under the
builder worktree's .local/vuh1498/.
net_host_cutscene_skip.json
uses the standard runner and native Start/Down/Cross skip controls. The host DLL
needs KH2COOP_EVENT_HOLD_PRODUCER=1; the client DLL and runtime need
KH2COOP_EVENT_HOLD_CONTROL=1 (the fixture supplies them). It checks input
suppression, exact resulting location, masked progress equality and fresh
movement after release. These flags remain off by default and native eligibility
remains empty-room-only. Natural playback of this known event lasts about 85 s;
its live profile reuses the existing MenuLiveness helper rather than relaxing
the gameplay hang watchdog. Synchronized playback, populated holds and
reconnect during a hold remain outside this result.
The standard fixture also passes: 20261013-063723_net_host_cutscene_skip_1, 111.4 s, 28 steps, including exact room/progress equality, suppressed then restored movement, and two reviewed client clips. Four saves unchanged and owned processes closed.
Host death before delayed wave allocation
net_spawn_emission_hb750_late_birth_3p.json reuses the corrected native750
geometry and friend-only entry control. Its spawn_late_birth step holds only
the runner's two original friend runtime HANDLEs, after checking executable and
bound game PID. It lets the host emit and kill the first Hammer2405 (record0),
waits for that exact host death publication, and resumes both transports in a
finally block. The hold is bounded to2000ms and always records recovery.
It changes no controller, cache, budget or save state.
Both friends must adopt that exact already-terminal Hammer2405, then consume all five wave receipts and match the four remaining native enemies' IDs and HP. After the host clears them, every peer must reach an empty native population and unmatched0. A run without terminal record0 adoption is insufficient. The helper preserves a JSON timing/receipt/recovery artifact even on failure.
Route friend movement at the BC wall (VUH-2305)
The full three-player route measures each friend's native displacement after 700ms forward input and700ms reverse input, taking the larger individual leg. This preserves the30-unit movement threshold when one direction hits the BC south wall. The identical probe runs during the active host cutscene hold and must remain below1 unit on both friends; the released boss check requires the native menu closed and more than30 units each. Route reports retain the start, forward and reverse positions and per-friend maxima. Count full-route passes across all attempted repeats; a partial segment pass is not a route pass.
Route revive placement in GoA (VUH-2317)
The final route segment places friend2 on the open GoA floor at1280,0,-100 and checks the settled position before the lethal hit. Camera-relative walking could leave the friend beside the computer, where the host's90-unit placement showed native Examine and correctly suppressed the revive prompt. After placing the host nearby, the fixture requires nativeRC0, a closed menu and revive range before pressing Triangle. It retains the1600ms hold and10-second revive timeout, records both positions and the native RC, and requires exactly one new prompt fire and native revive return. Runtime RC ownership and revive behavior are unchanged.
Route HB delayed-wave cleanup (VUH-2318)
After the Iron Hammers combat, clear_host_enemies polls every500ms and
kills living host combat enemies for up to30s, requiring3s continuously empty.
A new birth resets that quiet interval. This replaces two fixed kill sweeps,
which could finish before controller747 emitted its later Camo Cannons.
The report keeps the sampled populations and successful hit counts, including
partial results on timeout. The following20s all-peer empty check is unchanged:
the cleanup never hits friend actors, so retained friend enemies still fail.
Game emission, admission, HP and controller qualification are unchanged.
Prologue party: two Roxases from New Game (VUH-2464)
net_prologue_party.json (from its .gen.py) is the PC1 two-instance proof of
the prologue layout (PARTY_SETUP.md, "Prologue layout"). Relay and runtimes
connect at the title; both games run NEW GAME on the default rows; the host
plays the opening and the friend follows the host's first field from its own
blocked opening exit. It requires: both seats declared and acknowledged Roxas
during the opening; both at Tram Common evt95 and released there
([prologue] control released); per-seat Roxas census and clips; owner-only
movement through the route pilot (routes/prologue_move.json) filmed from the
other seat; one kh2ctl hit damage per seat with both SAVE ranges compared;
the Armor Shop event's same-room reload to evt97 (routes/prologue_armor_shop.json),
released on both; then the Day 1 lock-on tutorial: the Accessory Shop talk
(routes/prologue_accessory_shop.json and a Triangle press on reaction 31;
event 106 reloads the room as evt99) and the cat lock-on
(routes/prologue_cat.json; event 108 is the first room change, to the
Sandlot evt76). At the Sandlot it asserts two Roxases on each seat and no
[prologue] unpinned field line anywhere in the run, then repeats the
owner-only movement check (routes/prologue_sandlot_move.json).
net_prologue_party_skip.json is the same run with the opening and event
cutscenes skipped natively (Start, Down, Cross, only while that seat's field
gate is closed); it is for development, and the unskipped fixture stays the
acceptance evidence for the engine-driven scenes. net_prologue_party_sora.json and
net_prologue_party_sora_skip.json (VUH-1512) are the same checks with the friend on
KH2COOP_PROFILE=0x54: each seat expects the host's Roxas and the friend's Sora, one of
them local and the other a clone. The Roxas + Roxas fixtures also read each seat's clone
model color at first control: the host's copy of the friend wears seat 1's blue
(0x80F08050) and the friend's copy of the host seat 0's gold (0x805898B8). net_prologue_party_sora_reconnect.json takes
the Sora skip fixture to first control, drops the friend's runtime (runtime_stop) and
joins a new one. The friend must declare Sora again and be re-admitted by one host-led load
after the profiles agree, the host must not reload, and the friend's input must move the
host's Sora clone again. net_prologue_party_refusal.json (and its skipped
_refusal_skip) reruns the opening with the friend on KH2COOP_PROFILE=0x5B (Mickey)
and asserts that both seats hold at evt95 with unqualified-profile and log
profile 0x5B refused: Mickey, with one Roxas each, no clone or Sora stand-in, and
no movement or release; it films both screens, which carry the overlay's reason.
net_prologue_party_pc2_host.json and net_prologue_party_pc2_friend.json are
the two-PC acceptance run: the host on PC1 runs the relay on its Tailscale
address and the skipped route to the Sandlot; the friend runs on PC2 from a
sealed lane packet (tools/rig/pc2_lane/). Neither runner sees the other game:
each paces itself on its own copy of the other Roxas (moved more than 150 units)
and on its own [prologue] log, films the other seat moving, and the friend
reads its own HP and SAVE after the host has hit its copy of the friend.
Explicit peer ids keep the two runtimes distinct (each runner's game is instance
0). A PC1 rehearsal of both together (lane.py --rehearsal) passed before any
PC2 attempt.
The fixtures need two KH2 instances in Session 1 (desk bridge): about 20
minutes skipped, longer unskipped; the 2/1 evt56 opening stall (VUH-2338) can still cost an attempt.
The hit damage hit is synthetic native damage on a chosen actor, not an
enemy attack. A single held stick does not move Roxas while Tram Common's
tutorial box is up, which is why movement goes through the pilot's walk.
Two active combat pilots
A pilot step may name revivePeer: N, another earlier booted instance. It is
pad-only and extends that pilot's single input driver; do not also start
ReviveGuard. Each pilot observes its current PID's downed/revive receipts,
keeps a logically downed owner neutral (engine HP remains1), and approaches a
downed peer through ordinary stick input before holding Triangle within100
units. The hold is 1600 ms; reviveHoldMs changes it (and holdMs on a
revive_guard step), e.g. 5600 for Standard's 300-frame hold under
"revivePolicy": "native" (D17, VUH-2632). Native RCs cancel the hold immediately. Scope/actor/read failures stay
neutral and recovery attempts have a60-second limit. The peer's position is
read from its current canonical actor, independent of the cinematic camera.
net_twilight_thorn_rehearsal.json uses this on both seats with real doors and
no protect/immunity or memory-based kills. It records successful native revival
for any natural downing; no downing leaves revival unqualified. Its two-screen
30-second clip blocks explicitly disclose startup/encoder gaps and still need
caption assembly and visual review before being used as kill evidence.
Forcing a downing: downed_kill (VUH-2866)
{"do": "downed_kill", "instance": i} downs seat i on cue. It sends the
DownedSpike fixture channel's Kill (docs/DOWNED_REVIVE.md, "Test channel"):
native ApplyStatDelta to 0 HP, after which the product's own logical downing
takes over. The channel exists only in a game booted with
KH2COOP_DOWNED_SPIKE_FIXTURE=1 in that boot step's env; product play never
maps it. The step opens the mapping and never creates it, checks magic and
version 7, and waits up to timeoutMs (5000) for the DLL to handle the
request. It fails unless the result is Ok; "require": false records the
refusal instead (WrongState when already downed, InEvent, Branch...).
net_prologue_mansion_hall_{host,friend}_down.json use it in the Basement Hall.
Test: tests/test_downed_kill_step.py.
A downing proves "down before the wave" only if it came first. Review found
(VUH-2869) that the first draft of those variants forced the downing after
wave 1 and accepted a wave 2 that had already begun. The expression helper
downed_wave_order(downed, marker[, wave_object=309])
(tools/scenario/downed_order.py) orders each seat's own log after marker:
-
the one
[downed] kill actor=line and the fresh episode its intercept names; -
the line where each seat first names that episode;
-
each seat's first line about the wave's object;
-
the downed seat's later hold end, revive or return for that episode, or for a re-mint of it before any new downing (
docs/DOWNED_REVIVE.md, "Episodes": after 30 s without a request the same downing ends under the re-minted number). -
in the host's own log, the episode before wave 1's third death. The Basement Hall spawns its two 309s natively at that third host death, not the fourth (fixtures 194057/194738), and the 309 lines lag it by tens of lines. Run
20261026-225539(VUH-2901) logged death, then Kill, then the first 309, an order the birth lines alone would have accepted. The episode exists only after the Kill, so the host naming it first proves the Kill came before the trigger.
It returns ok/why, episodeSeen, waveStarted, triggeredFirst and
ended. The variants stop wave 1's fight at its first death, hold still for a
1.5 s settle, and refuse to continue if wave 2 has started or a third death has
come. They then down the seat, wait until both seats name the episode, and
re-check that neither wave 2 nor its trigger came first. Only then does the
other seat finish both waves.
The stop used to be the second death. A hit already in flight when the fight
stopped then killed the third Dusk before the Kill twice (host_down
20261027-062703, friend_down 20261026-225539, VUH-2901): the guard refused
the first, and the second predated it. Stopping at the first death leaves a
margin of two kills for those in-flight hits, and the settle, with no input,
lets the guard count any of them before the Kill instead of racing it. The guard
itself (< 3 deaths, no 309 line) is unchanged, so an overshoot still fails the
run before the downing. The end requires ok. Test:
tests/test_downed_wave_order.py, which covers the checker and the variants'
own steps through the runner's Context.
The host-down variant leaves a single stand-in to finish wave 1 and fight all of
wave 2. Twice (20261026-200433, -220631) native Dusk damage downed it as
well, before any 309 spawned. That made a full-party down, and the variant never
reached the wave it exists to test. So from the confirmed downing until the
wave-2 fight ends, that seat is protected ({"do": "protect", "instance": 1},
then "on": false). Protection makes the friend's mirrored Dusks
miss it, but the friend still lands its hits as claims
(net_client_hit_claims.json). The host-down variant therefore claims nothing
about the friend taking hits.
The friend-down variant's lone host needs the same aid (VUH-2901). In
20261027-063606 the forced episode was ordered correctly, then native Dusk
damage downed the host after five of six deaths (one 309 left at 28 HP), before
the friend's return was due, and the native game over followed. That is a bot
losing a solo fight, not a revive or return failure (VUH-2866 comment
8d1820c1). So over the same window the host is protected with
"afterPilot": true: its route pilot finished at the hall, and from there a
fight bot plays it. The variant claims nothing about the host taking hits, and
its fullPartyDown == 0 check cannot fire while the host is protected. The
friend's own hold end, revive or return for the forced episode is still
required. The forced downing, its order before wave 2, and
the 6/6 deaths and end checks are unchanged.
Waiting for the friend's arrival before coop_verdict (VUH-2898)
coop_verdict judges the logs as they are when it runs, and the host's latest
room must have a friend arrival. The Mansion scenarios end right after both
seats read Axel I's room (02/14). They tore down 0.9 s (20261026-215808) and
1.1 s (-221605) after the host arrived, but the friend arrives 5.64 to 5.75 s
after the host (193245, 194057, 194738, 201309). So the verdict failed
no friend arrival on a friend that was still loading.
The step's "arrivalTimeoutMs": N first waits up to N ms until every friend has
logged client arrived for the host's latest epoch, and its first statehash
there. If a friend is still missing at the limit, the step fails as before, and
the message names how long it waited. The three Mansion scenarios use 20000 ms:
about 3.5 times the worst measured lag, from a wait that starts about 1 s after
the host's arrival. That leaves room for slower loads and still bounds the run.
Each verdict room row now records arrival timing, which is reported, not judged:
hostTickMsandfriendTickMs: each seat's first[statehash] ... tickMs=stamp in that epoch, at or after its arrival line;lagMs: the friend's lag behind the host.
tickMs is GetTickCount, so only seats on one PC share it. A seat may log its
first stamp up to its once-a-second cadence after arriving, so treat lagMs as
±1 s. The step result's lastRoom carries the latest room's row per friend.
Tests: tests/test_mansion_verdict_wait.py and tests/test_coop_verdict.py.
Thorn fight routes may opt into blockedThornProbe: after one second with less
than15 units of canonical movement just outside melee reach, a selected live307
can receive one native jump and at most two Cross taps. Three attempts and a
three-second cooldown bound the experiment; RC, revive, tutorial, damage and
scope changes preempt it. This does not prove head geometry or damage. The
fixture explicitly uses lowHp:0 so a25% native revival can resume attacks;
ordinary guard and recent-damage escape remain, with full incoming damage.
The Thorn scenario checks its existing owned background pilot processes after each finalized paired clip block. A failed pilot stops further recording; the current clips and samples remain collected. This avoids recording several minutes after combat automation has already failed, without adding another input driver or runner API. Kill, RC, damage, revive and exit assertions remain unchanged.
The Twilight Thorn rehearsal's live08 observation reads existing version7 read-only downed prompt fields from the paired DLL. It uses the product's qualified actionable RC for pilot Triangle decisions, retaining raw cached RC as diagnostic data. Pilot routes/reach/timing/strategy are unchanged. Both native RC adapters must report successful installation before the route; this assertion and the existing kill/reward/exit checks remain strict.
A pilot step may set externalEvidence: true when the scenario owns that
seat's recordings. The pilot still releases input, logs the failure and exits
with the same status; it skips only its best-effort failure screenshot/clip.
Use this for background combat pilots beside paired clip steps, including
Thorn, so their failure handler cannot contend with an active recorder. The
default retains standalone pilot failure evidence.
For a short milestone proof, pilot_until monitors exactly two existing named
pad pilots and their paired 30-second background clips. Supply pilots, an
until evidence expression, an abort expression, timeoutMs (at most150000),
everyMs:100, abortEveryMs:1000, maxBlocks (at most6), two clips and as.
Start the initial indexed clip pair before the pilots. The step stops and reaps
both exact input children before either neutral release, checks release status,
then drains the active clips without starting another pair. Natural pilot exit,
timeout, game death, abort or capture failure fails the proof. Existing
pilot_wait and clip_wait behavior is unchanged.
net_twilight_thorn_reactions.json uses this for the exact trimmed RC build:
opening297/298/299 and ground345/432 must complete on the appropriate owners,
with a matching checked client send and host callback for the friend. It stops
at that milestone; it is not a kill fixture. Actual mission/room checks and a
sampled128HP boss floor abort a failed qualification before continuing toward
a kill. Its Session1 read-only state trace retains raw/qualified reaction fields
and process identity; encoding gaps and sampling jitter remain explicit. The
separate full-kill rehearsal keeps its original victory/reward/exit checks.
