# Phase A — ours (xenia-rs) changes All changes are **additive and cvar-gated default-off**. With the cvar unset, `xenia-rs check --stable-digest -n 50000000` produces a byte-identical digest to the pre-patch baseline (verified in `validation.md`). | File | Change | Why | |---|---|---| | `crates/xenia-kernel/src/event_log.rs` | **NEW** (≈340 LOC) | Phase A emitter. Lazy file open, per-tid monotonic `tid_event_idx` counter, FNV-1a 64-bit `semantic_id`, JSONL writer with `Mutex>`. `is_enabled()` is a relaxed atomic-bool load → zero overhead when disabled. Includes unit tests for FNV-1a vs the standard `"foobar"` test vector and for `semantic_id` stability. | | `crates/xenia-kernel/src/lib.rs` | +1 line (`pub mod event_log;`) | Register the new module. | | `crates/xenia-kernel/src/state.rs` | +33 LOC inside `call_export` | Single hook site for `import.call` / `kernel.call` / `kernel.return`. All three emits are inside `if phase_a_on` guards. Reads `tid` and `cycle_count` from the running thread when enabled. | | `crates/xenia-app/src/main.rs` | +1 CLI flag, +6 LOC dispatch | Adds `--phase-a-event-log ` to the `Exec` subcommand. Env-var fallback `XENIA_PHASE_A_EVENT_LOG`. Calls `xenia_kernel::event_log::init(path)` once at startup; `None` keeps the emitter disabled. | ## Total surface area ≈ 380 LOC additive across 4 files; no existing logic modified. ## Hook coverage (v1) - `import.call` — emitted at the syscall dispatcher (`call_export` in `state.rs`) once per kernel/import invocation, before `kernel.call`. - `kernel.call` — same site, after `import.call`, before the export body runs. - `kernel.return` — same site, after the export body returns, with `r3` as the integer return value. - `schema_version` header — emitted once on file open (synthetic `tid=0`). The following event kinds are part of schema v1 but **not yet wired** in either engine in this phase. Some have a Rust emitter function ready (function exists in `event_log.rs`, no call site); others have no Rust function yet: - `thread.create`, `thread.exit` — emitter ready (`emit_thread_create`, `emit_thread_exit`), no call sites. - `thread.suspend`, `thread.resume` — declared in schema, no Rust function yet. - `handle.create`, `handle.destroy` — emitter ready (`emit_handle_create`, `emit_handle_destroy`), no call sites. - `wait.begin`, `wait.end` — emitter ready (`emit_wait_begin`, `emit_wait_end`), no call sites. - `mem.write` — declared in schema, no Rust function yet; gated behind a separate cvar (`phase_a_event_log_mem_writes`) declared in canary defaulted false. - `vfs.open`, `vfs.read`, `vfs.close` — declared in schema, no Rust function yet. Wiring any of these is additive and can land in a follow-up without touching the schema or the diff tool. ## Verification ```bash cd "/home/fabi/RE - Project Sylpheed/xenia-rs" # Build cargo build --release -p xenia-app # Unit tests (FNV-1a + semantic_id stability) cargo test -p xenia-kernel event_log # Cvar-OFF determinism cp target/release/xenia-rs target/release/xenia-rs-phaseA-post target/release/xenia-rs-phaseA-post check --stable-digest -n 50000000 \ --out /tmp/digest-after.json \ "/home/fabi/RE - Project Sylpheed/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso" diff audit-runs/phase-a-diff-harness/digest-pre-patch.json /tmp/digest-after.json # expect empty diff (byte-identical) # Cvar-ON sanity run rm -f audit-runs/phase-a-diff-harness/ours-sanity.jsonl target/release/xenia-rs-phaseA-post exec -n 50000000 \ --phase-a-event-log audit-runs/phase-a-diff-harness/ours-sanity.jsonl --quiet \ "/home/fabi/RE - Project Sylpheed/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso" head -1 audit-runs/phase-a-diff-harness/ours-sanity.jsonl # must start with schema_version ```