Files
xenia-rs/audit-runs/cache-subsystem-plan/persistent-experiment.md
MechaCat02 ef93a4fa14 handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes
Source changes (dormant parity infra, retained from iterate 2.AI/2.AO):
- xenia-kernel/exports.rs: nt_create_event manual_reset polarity +
  related event wiring
- xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity

Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the
iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps
(.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as
regenerable local artifacts — see memory + HANDOFF for the running findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 07:19:08 +02:00

6.3 KiB

Persistence experiment — XENIA_CACHE_PERSIST=1 impact on matched-prefix

Setup

  • Binary: xenia-rs/target/release/xrs-c10 (active C+10 build, both engines).
  • Window: 50M instructions per boot.
  • Flag: XENIA_CACHE_PERSIST=1; mount falls back to $HOME/.local/share/xenia-rs/cache because XDG_DATA_HOME is empty on the host.
  • Comparison baseline: C+10 default-tmpdir digest instructions=50000002 imports=40465 swaps=1 draws=0 (with stable-digest md5 b8fa0e0460359a4f660adb7605e053de); event log xenia-rs/audit-runs/phase-c10-NtQueryFullAttributesFile/canary.jsonl.
  • Diff tool: xenia-rs/tools/diff-events/diff_events.py --tid-map 6=1.

Boot-by-boot digests

boot mode instructions imports swaps draws
C+10 baseline (default tmpdir) wipe-on-boot 50000002 40465 1 0
Persist boot 1 (cold persist) persistent 50000003 40485 1 0
Persist boot 2 (warm persist) persistent 50000003 40485 1 0

The persistence path is +1 instruction / +20 imports over the default-tmpdir baseline. Warm boot is identical to cold boot at the digest level (no regression observed at 50M; AUDIT-053's regression was at 500M+).

On-disk cache state per boot

After persist boot 1 (cold)

Total: 7 files / 1.4 MB.

d 4096  /access      ← BUG: should be 240 B file (canary)
d 4096  /ignore      ← BUG: not in canary's cache
d 4096  /recent      ← BUG: should be 160 B file (canary)
d 4096  /d4ea4615
d 4096  /d4ea4615/e
d 4096  /69d8e45c
d 4096  /69d8e45c/9
d 4096  /69d8e45c/e
d 4096  /aab216c3
d 4096  /aab216c3/5
d 4096  /aab216c3/a
f 2400  /d4ea4615e46ee8ca.tmp
f 5784  /69d8e45ce534ffea.tmp
f 12288 /69d8e45c9355f2f8.tmp
f 12288 /69d8e45c973a5c0a.tmp
f 12288 /aab216c35ee70e0a.tmp
f 614400  /aab216c3a2c8c185.tmp
f 685464  /69d8e45c939a9dcc.tmp

Notable:

  • access, ignore, recent exist as DIRECTORIES (bug #2).
  • .tmp flat journals exist (cache-build path is firing).
  • Hash subdirectories exist (AUDIT-054's FILE_DIRECTORY_FILE handling works).
  • Hierarchical leaf files (e.g. d4ea4615/e/46ee8ca) do not exist — the .tmp → leaf rename is being silently dropped (bug #1).

After persist boot 2 (warm)

Total: 7 files / 1.6 MB (.tmp files grew).

file boot 1 boot 2 growth canary's equivalent leaf size
d4ea4615e46ee8ca.tmp 2400 2800 +400 400 B (d4ea4615/e/46ee8ca)
69d8e45ce534ffea.tmp 5784 6748 +964 964 B (69d8e45c/e/534ffea)
69d8e45c9355f2f8.tmp 12288 14336 +2048 2048 B (69d8e45c/9/355f2f8)
69d8e45c973a5c0a.tmp 12288 14336 +2048 2048 B (69d8e45c/9/73a5c0a)
aab216c35ee70e0a.tmp 12288 14336 +2048 2048 B (aab216c3/5/ee70e0a)
aab216c3a2c8c185.tmp 614400 716800 +102400 102400 B (aab216c3/a/2c8c185)
69d8e45c939a9dcc.tmp 685464 799708 +114244 114244 B (69d8e45c/9/39a9dcc)

The per-boot growth of each .tmp file exactly matches the byte size of the corresponding canary hierarchical leaf. Strong indirect evidence that the .tmp contains the same data as canary's leaf but is being appended on each boot instead of being renamed-to-leaf and consumed.

This is exactly AUDIT-053's "journal-style appends per boot" pattern. AUDIT-053 predicted a runtime_error regression at warm-start because the version header would go stale; in the 50M window of this experiment, that regression has not yet manifested (AUDIT-054's report had it at 500M+).

Phase A diff vs C+10 canary baseline

Command:

python3 xenia-rs/tools/diff-events/diff_events.py \
  --canary xenia-rs/audit-runs/phase-c10-NtQueryFullAttributesFile/canary.jsonl \
  --ours   xenia-rs/audit-runs/cache-subsystem-plan/persist-warm-events.jsonl \
  --tid-map 6=1

Result:

| canary_tid | ours_tid | matched | canary_total | ours_total | first_divergence_at |
|---|---|---|---|---|---|
| 6          | 1        | 102404  | 315020       | 108471     | 102404              |

Matched-prefix unchanged from C+10 baseline (102404).

Divergence event:

canary [6][102404] kernel.return NtQueryFullAttributesFile return_value=0
ours   [1][102404] kernel.return NtQueryFullAttributesFile return_value=0xC0000034

Pre-context shows both engines reach the same path query cache:\d4ea4615\e\46ee8ca byte-for-byte, but ours returns NOT_FOUND because that leaf doesn't exist on disk (only the flat .tmp does).

Conclusion

Persistence is necessary but not sufficient. The plan's Stage 1 (rename fix) is required to convert .tmp flat journals into hierarchical leaves. Stage 2 (top-level file misclassification) is required to fix the access/ignore/ recent directory-vs-file bug. Stage 3 (flip default) follows after both bugs are addressed.

Expected matched-prefix advance after all three stages: hundreds-to-thousands of events, until a non-cache divergence appears.

Reproduction commands

cd "/home/fabi/RE - Project Sylpheed"

# Clean cache for cold start
rm -rf ~/.local/share/xenia-rs/cache

# Boot 1 (cold)
XENIA_CACHE_PERSIST=1 ./xenia-rs/target/release/xrs-c10 check \
  -n 50000000 --stable-digest --out /tmp/digest-boot1.json \
  "Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso"

# Inspect resulting cache layout
find ~/.local/share/xenia-rs/cache -mindepth 1 -printf '%y %s\t%p\n' | sort

# Boot 2 (warm) — same command, no rm
XENIA_CACHE_PERSIST=1 ./xenia-rs/target/release/xrs-c10 check \
  -n 50000000 --stable-digest --out /tmp/digest-boot2.json \
  "Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso"

# Boot 2 with Phase A event log
XENIA_CACHE_PERSIST=1 ./xenia-rs/target/release/xrs-c10 exec \
  -n 50000000 --quiet \
  --phase-a-event-log xenia-rs/audit-runs/cache-subsystem-plan/persist-warm-events.jsonl \
  "Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso"

# Diff
python3 xenia-rs/tools/diff-events/diff_events.py \
  --canary xenia-rs/audit-runs/phase-c10-NtQueryFullAttributesFile/canary.jsonl \
  --ours   xenia-rs/audit-runs/cache-subsystem-plan/persist-warm-events.jsonl \
  --tid-map 6=1

State at session end

~/.local/share/xenia-rs/cache/ is left in its post-boot-2 state (7 .tmp files, ~1.6 MB, 3 directories that should be files, 7 empty hash subdirs). Next session should rm -rf ~/.local/share/xenia-rs/cache before Stage 1 work to start from a clean slate.