1@README.md 2 3## Notes for agents 4 5- **Frame numbering is per-branch-relative to a bot lifetime.** A branch's 6 log entry at local index `i` was recorded during the tick where 7 `zbanks::Bot`'s own internal frame counter was `i` - true by construction, 8 because a recording always starts exactly when a fresh `Bot` does (window 9 start, or a rebuilt branch). `src/recorder.rs` and `apps/native`'s 10 integration must preserve this: never start a recording or a branch 11 mid-bot-lifetime, or `src/rebuild.rs`'s goal-choice replay (keyed to tick 12 order, not a stored frame number) silently mismatches with no error. 13- **A non-root branch's own keyframe at its fork frame is not optional.** 14 `crate::tree::Tree::owner_of` resolves a frame exactly at a fork point to 15 the CHILD, on the assumption that the child always has its own keyframe 16 there (`src/recorder.rs`'s `store_fork_keyframe`). Skip that call when 17 creating a branch and seeking to its very first frame silently reads the 18 wrong keyframe file and panics on a missing one instead. 19- **`keyframes.rs` bounds seek cost by tier, and pruning is only safe 20 because of one invariant:** a keyframe delta-references only a keyframe of 21 its own tier or coarser, never a finer one. That is what lets `prune` 22 delete a whole aged-out dense or medium bucket without orphaning a 23 survivor's reference. Change `preferred_reference` and pruning silently 24 becomes a way to corrupt keyframes. `prune` also never deletes a branch's 25 first (anchor) keyframe, whatever its tier. 26- **Keyframes are `Console::snapshot_fixed`, never `Console::snapshot`.** 27 The keyframe codec (`src/keyframe.rs`) XORs at fixed offsets, and varint 28 snapshots shift every field whenever one integer crosses a width 29 boundary: 2 s deltas were 866 KB that way, 115 KB fixed-width. The 30 recorder takes `&Console` and snapshots itself so a caller cannot hand it 31 the wrong kind, and `seek.rs` restores keyframes with `restore_fixed` but 32 the header's frame-0 snapshot with `restore`. Changing a keyframe's 33 encoding means bumping `header::FORMAT_VERSION`: older recordings are 34 then deleted at window start (`storage::remove_other_formats`), not read. 35- **The compression is `ruzstd` (pure-Rust zstd) over the XOR.** If disk 36 per hour needs to come down, the knobs are `recorder::RECORD_EVERY`, the 37 tier retention constants in `keyframes.rs`, and `storage`'s cap - measure 38 with `apps/replay-probe` before and after. 39- **`seek.rs`, `rebuild.rs` and `storage.rs` were built only after 40 `apps/replay-probe` proved the core deterministic** - two independent 41 10,000-frame replay passes, byte-identical to a bot-driven recording and 42 to each other (2026-09-22). All three assume that; if a future core 43 upgrade needs re-proving it, rerun the probe before trusting any of them 44 again - a diverged core makes every one of them silently produce a wrong 45 machine, not a loud error. 46- **`seek::entries_up_to` is for `rebuild.rs`; `seek_to` is for scrubbing.** 47 They are not interchangeable: `seek_to` starts from a keyframe ALREADY 48 inside the target branch and only needs that branch's own tail; 49 `entries_up_to` starts from frame 0 and stitches every ancestor branch's 50 contribution together, because a fresh bot has no keyframe to jump to - 51 it must tick every logged frame from the beginning. 52- **Never snapshot on every frame "to be safe".** A snapshot is 1.3 MB; 53 `recorder::Recorder::record` takes the console and only snapshots on the 54 frame a keyframe is due. 55- The design notes behind this crate (Doom, Quake, Source, BizHawk, 56 RetroArch; "determinism first") live in the owner's private notes, as 57 `replay-timeline.md`; source comments cite them by that name. They are not 58 in this repository, so do not add links to them here.