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>
4.5 KiB
Phase Host-Audio-Eager — Re-validation (2026-05-19)
Progression metric (primary gate)
| metric | pre-fix baseline | post-fix | delta |
|---|---|---|---|
| swaps | 1 | 1 | 0 |
| draws | 0 | 0 | 0 |
The progression metric did NOT move. Despite landing the eager-seed
implementation cleanly with 3× reproducibility, neither swaps nor draws
advanced. This matches the prior agent's diagnosis: the audio worker
ordering issue is real, but the deeper root cause is voice-struct state
divergence — the audio callback at 0x824D6640 in ours blocks on
KeWaitForMultipleObjects([0x82928B04, 0x82928AE0]) immediately
because the voice struct at [r31+356] reads 0x01 (ours) vs 0x00
(canary). Pre-seeding 8 fires lets try_inject_audio_callback deliver
the first callback earlier, but the callback still blocks on the same
guest dispatchers — fires 2-8 sit in the queue because
interrupts.is_in_callback() stays true.
3× determinism
73e99d60029128b4d5c3dd98e540457d82a52b8a962e7495132be2be31411aca /tmp/digest_eager_1.json
73e99d60029128b4d5c3dd98e540457d82a52b8a962e7495132be2be31411aca /tmp/digest_eager_2.json
73e99d60029128b4d5c3dd98e540457d82a52b8a962e7495132be2be31411aca /tmp/digest_eager_3.json
All three cold runs produce byte-identical digest JSON. The seed-at-register implementation is fully deterministic in lockstep mode (the ticker accumulator gets pre-populated synchronously inside the register handler, no host-thread non-determinism).
Digest JSON
{
"instructions": 50000007,
"imports": 40390,
"unimpl": 0,
"draws": 0,
"swaps": 1,
"unique_render_targets": 0,
"shader_blobs_live": 0,
"texture_cache_entries": 0
}
imports/unimpl unchanged from the C+22 baseline (40390/0).
Phase B invariant
image_loaded_sha256 = ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18
UNCHANGED — Phase B is not affected by audio-runtime changes.
Per-chain matched-prefix
100M-instruction cold trace vs canary baseline
(xenia-rs/audit-runs/phase-d-stage1/canary-cvaroff-trunc.jsonl —
pre Phase D D-extension absorber, so main reads at the C+18
102,424 value, NOT the post-D-extension 105,046).
| chain | pre-fix | post-fix | delta | first divergence |
|---|---|---|---|---|
| canary tid=4 → ours tid=11 | 11 | 11 | 0 | (preserved) |
| canary tid=6 → ours tid=1 | 102,424 | 102,424 | 0 | NtQueryFullAttributesFile (C+18-era) |
| canary tid=7 → ours tid=2 | 32 | 32 | 0 | (preserved) |
| canary tid=12 → ours tid=7 | 4 | 4 | 0 | C+23 idx=4 |
| canary tid=14 → ours tid=9 | 41 | 41 | 0 | (no advance — primary target) |
| canary tid=15 → ours tid=10 | 16 | 16 | 0 | (no advance — primary target) |
The two primary targets (tid=14→9 and tid=15→10) were the audio worker guest threads spinning on the uninitialized voice struct. Their matched-prefix did NOT advance.
Kernel tests
- Pre: 217 passed
- Post: 221 passed (+4 new
seed_fires_for_*tests) - Failures: 0
- All existing tests pass
Build
cargo build --release clean. One pre-existing dead-code warning
unrelated to this fix.
Total LOC
| file | added | removed |
|---|---|---|
crates/xenia-kernel/src/xaudio.rs |
86 | 0 |
crates/xenia-kernel/src/exports.rs |
18 | 5 |
| total | 104 | 5 |
Net ~100 LOC, of which ~60 LOC are tests + doc comments. Engine logic delta is ~25 LOC.
Conclusion
The implementation lands cleanly:
- 3× cold-deterministic
- Phase B unchanged
- All tests pass
- Sister chains preserved
But the progression metric (swaps/draws) did NOT move. This is an HONEST NEGATIVE RESULT: the eager-seed approach addresses the symptom (ticker delays the first callback) but not the root cause (the callback at 0x824D6640 still blocks on guest dispatchers that only tid=9/10 can signal, and tid=9/10 are stuck on a voice-struct field that the callback would need to clear — but doesn't, because canary's callback takes a DIFFERENT control-flow path that doesn't reach the KeWaitForMultipleObjects in the first place).
The deeper fix requires either:
- Identifying the guest write that initializes
[r31+356]to 0 in canary's boot path and ensuring ours produces the same write. - A true host-side audio worker thread that can run the callback in a host context (substantial threading-model rework).
Both are out of scope for this session per the brief's "Don't widen scope" tripstone.