uv run crimson original divergence-report artifacts/frida/share/gameplay_diff_capture.json.gz --float-abs-tol 1e-3 --window 24 --lead-lookback 1024 --run-summary-short --run-summary-focus-context --run-summary-focus-before 8 --run-summary-focus-after 4 --run-summary-short-max-rows 30 --json-out analysis/frida/reports/capture_49aec5d3_baseline.json(expected non-zero exit while diverged)
uv run crimson original bisect-divergence artifacts/frida/share/gameplay_diff_capture.json.gz --window-before 12 --window-after 6 --json-out analysis/frida/reports/capture_49aec5d3_bisect.json(expected non-zero exit while diverged)
uv run crimson original verify-capture artifacts/frida/share/gameplay_diff_capture.json.gz --float-abs-tol 1e-3 --max-field-diffs 32(expected non-zero exit while diverged)
uv run crimson original focus-trace artifacts/frida/share/gameplay_diff_capture.json.gz --tick 1390 --near-miss-threshold 0.35 --json-out analysis/frida/reports/capture_49aec5d3_focus_1390.json
Primary actionable lead is the first RNG-tail shortfall at tick 1390 (caller_static=0x004263b1, mapped in divergence lead as creature_update_all path).
Next probe should focus on replay/runtime branch parity in creature/projectile update flow around tick 1390, especially:
New first mismatch is a transient one-tick Reflex Boost timer rounding drift:
tick 5305: bonus_timers.9 expected=81 actual=82,
self-heals on the next tick (tick 5306).
Failed local probes (not landed):
applying the same scaled-ms conversion change in run_deterministic_step (no net gain),
hybrid <1.0s fallback in survival session (reintroduced one-tick RNG spawn shift at 5307/5308).
Current working hypothesis for residual timer drift:
native player_update temporarily mutates global frame_dt and restores it with *1.6666666, introducing tiny float drift before bonus_reflex_boost_timer decrement,
rewrite currently models movement dt locally and does not propagate this global post-player-update drift.
In SurvivalDeterministicSession.step_tick, when capture dt_frame_ms_i32 is present and Reflex Boost scaling is active, derive session ms counters from scaled float dt (int(dt_sim * 1000.0)) instead of integer-base ms scaling.
This matches native survival wave cadence in the tick 5226 window.
tests/test_replay_runners.py
Added test_survival_runner_original_capture_reflex_scaled_dt_ms_uses_scaled_float_dt to lock the scaled-float ms behavior for original-capture replay under Reflex Boost time scaling.
A tiny Reflex Boost tail-rounding bias remained upstream of the old tick 8999 RNG shortfall.
At tick 8915, capture and replay quest cadence differed by one ms (capture +21 vs rewrite +20).
Bias sweep around _REFLEX_TIMER_SUBTRACT_BIAS showed 4e-9/5e-9/1e-8 all remove that cadence mismatch and eliminate the tick 8999 RNG-tail shortfall.
With the Reflex timer fix in place, first sustained divergence moved to tick 9065 and became XP-only (+110 replay).
focus-trace at 9065 shows replay-only Splitter branch (proj 17 hit creature 17, spawning children idx 9/11) while capture shows five misses for proj 17.
Root-cause chain for the 9065 branch was traced backward through spawn ancestry:
9065: proj 17 replay hit vs capture miss,
9043: same slot hits one query earlier in replay, creating translated child spawn positions,
9041/9030: translated Splitter-hit spawn positions persist across generations,
9014: parent proj 4 hit timing differs because target creature 31 position is already far from capture (~+6.39,+1.50 in replay at the decisive query),
9013: initial player projectile spawn for this chain is near-identical to capture (drift is not introduced at fire spawn).
This isolates the remaining failure as upstream creature-motion/AI parity drift, not a local Splitter branch implementation bug.
Reflex-tail parity improvement is landed and moved divergence later (8999 -> 9065), but the remaining blocker is upstream creature-motion drift causing earlier/later hit resolution in long Splitter chains.
Current capture is insufficient to localize the first creature-motion branch split in replay with confidence from existing per-tick summaries alone.
Next probe should add creature update micro-tracing for focused ticks (movement candidate selection + per-step target heading/obstacle decisions) and recapture:
native creature_update movement branch decisions,
rewrite src/crimson/creatures/runtime.py + src/crimson/creatures/ai.py parity at the first ancestry tick where creature position drift appears.
first large HP branch split appears later at tick 9011 (hp_delta=-125.294), then feeds the known 9014 -> 9043 -> 9065 chain.
Slot-31 behavior in the onset window is not an orbit-branch issue:
rewrite force_target=1 through the early drift window (~8890..8974), so movement is player-chase steering in those ticks.
Decompile + structural queries were used to re-check native movement/turn paths:
creature_update_all (0x00426220) and angle_approach (0x0041f430) loops/conditions,
survival_spawn_creature confirms spawn writes heading/velocity/move_speed but leaves other fields as recycled-slot state.
Multiple targeted A/B probes were run via runtime monkeypatches with no material improvement on the slot-31 drift profile:
orbit helper intermediate-rounding changes,
distance helper rounding changes,
stricter float32 arithmetic variants in angle_approach,
heading_from_delta_f32 f32-cast input deltas,
dt resolve f32-cast,
spawn/init scalar f32 canonicalization in _apply_init.
Conclusion: we can localize the drift timeline from current capture, but not disambiguate the first causative arithmetic/branch delta in native without finer-grained creature-update internals.
Hard block with current capture granularity: first visible slot-31 drift is sub-ULP-scale and accumulative (8736+), but current telemetry lacks per-creature pre/post movement internals to isolate which native arithmetic/branch diverges first.
Next required capture probe should add creature-update micro-telemetry for targeted slots/ticks: