Trace format alignment¶
The debugging pipeline has one current contract shared by the three producers used for parity work:
- Original executable capture through Frida JSONL, finalized by
src/crimson/dbg/frida_finalize.py. - Python replay recording through
src/crimson/dbg/record.py. - Zig replay recording through
crimson-zig/src/cdt_trace.zig.
Frida JSONL remains a producer-private transport. Once a run becomes a .cdt,
all consumers see the same typed tick data and no producer-specific aliases.
Current-only contract¶
| Artifact | Current version | Authority |
|---|---|---|
| Frida raw JSONL | 22 | scripts/frida/gameplay_diff_capture.js |
| Frida evidence sidecar | 2 | src/crimson/dbg/frida_finalize.py |
| CDT container | 2 | src/crimson/dbg/schema.py |
| CDT payload schema | 15 | src/crimson/dbg/schema.py |
| CRD replay | 16 | src/crimson/replay/types.py |
These artifacts are throwaway debugging data. Readers and finalizers require exactly these versions; they do not translate, normalize, or salvage an older recording. Regenerate an original capture with the current Frida script when a version changes.
Canonical tick¶
Every TickRecord contains:
tick_indexelapsed_msdt_ms_i32mode_idchannels
Schema 15 requires every channel on every tick:
replay_stepcheckpointsim_stateentity_samplesrng_streamtiming_samples
The core channel types live in
src/crimson/dbg/canonical_channels.py; Zig mirrors the same wire schema in
crimson-zig/src/cdt_trace.zig.
Replay-driving evidence¶
replay_step is the first channel because it records what drove the tick, not
only what existed after the tick:
dt: the exact f32 frame delta used by replayinputs: one input row per player with movement, aim, and flagsprelude: ordered native frame-RNG advances and perk operations applied before simulationpostlude: perk-menu generation applied after simulation while tick RNG tracing remains activecommands: Typ-o commands applied as part of the tick
The generated .crd sidecar and replay-recorded .cdt both derive from this
same payload. There is no separate replay_inputs field in the raw tick
contract.
Movement state¶
Each player in sim_state preserves the movement and aim state needed to
localize a control or integration mismatch:
headingmove_speedmove_phaseaimaim_heading
These fields complement the input intent in replay_step: input differences
identify the cause before simulation, while state differences show their
effect.
Strict invariants¶
The owned formats fail at the first contract violation:
- unknown typed fields are rejected
- CDT tick indices are strictly increasing and unique
- Frida run-local and session-global tick indices are contiguous
- tick, checkpoint, timing, mode, and player counts must agree
replay_step.inputsandtiming_samplesmust be non-empty- every tick has exactly one
gpur_entertiming sample gpur_enter.frame_dt_f32equalsreplay_step.dtgpur_enter.frame_dt_ms_i32equalsTickRecord.dt_ms_i32- RNG rows use one-based call indices and valid CRT state transitions
- a Frida stream must close every run; EOF after
run_endis complete
An error/run_error, truncated lifecycle, out-of-sequence row, or mismatched
counter invalidates the capture. Partial-run finalization would turn missing
evidence into an apparent behavior difference, so it is intentionally not
supported.
Producer boundaries¶
Frida original capture¶
Capture format 22 emits typed lifecycle rows and canonical tick channels. The finalizer validates them, writes one CDT/CRD pair per completed run, and writes a sibling typed evidence sidecar containing the producer-only rows used to explain how canonical values were derived. The CDT, CRD, RNG report, and typed evidence sidecar publish as one rollback-safe artifact bundle.
Run-start creature-pool residue is copied into the replay header when present. That preserves original state which survives the native reset and avoids reconstructing it from the first post-tick snapshot.
Python replay recorder¶
Python records the same replay step, checkpoint, simulation state, entity, RNG,
and timing evidence while it executes a CRD replay. Metadata identifies the
replay fingerprint and implementation, and is validated through the same typed
TraceMeta contract.
Zig replay recorder¶
Zig writes the same CDT v2/schema 15 chunks and channel payloads. Use
crimson-zig dbg record <replay.crd> --out <trace.cdt> to record and
crimson-zig dbg verify to check that its compiled schema and replay versions
match the owned contract.
Differential workflow¶
Run dbg verify after changing any owned format; it prints the complete current
Frida, evidence, CDT, replay, and checkpoint version matrix, then checks the
Python, Frida, and Zig source declarations, tick-boundary field order, required
channels, and replay/checkpoint payload ceilings for drift.
Run dbg health on both traces before interpreting a diff. Health validates the
tick records, reports tick spans and gaps, counts rows per channel, and exits
nonzero when the selected window is not ready for parity analysis.
dbg diff then compares the driving step before post-tick state:
replay_stepcheckpointrng_streamsim_stateentity_samplestiming_samples
The top-level mismatch remains the earliest divergent tick. The report also
contains channel_first_mismatches, so one run exposes the first bad tick for
each channel instead of hiding downstream evidence behind the earliest one.
channel_first_diagnostics carries non-behavioral evidence.
RNG caller labels are attribution metadata. If draw values and state
transitions match but caller labels differ, the result is an
rng_caller_attribution_mismatch diagnostic, not behavioral divergence.
Strict numeric mismatches include the numeric delta, both f32 bit patterns, and the f32 ULP distance. This makes one-bit precision drift distinguishable from a different simulation decision without returning to producer logs.
Use dbg bisect to bound the earliest bad tick and dbg focus, dbg tick,
dbg entity, or dbg query to inspect the relevant channel and entity history.