diff --git a/src/xenia/cpu/cpu_flags.cc b/src/xenia/cpu/cpu_flags.cc
index 3ff067e15..1abad3bd2 100644
--- a/src/xenia/cpu/cpu_flags.cc
+++ b/src/xenia/cpu/cpu_flags.cc
@@ -57,3 +57,54 @@ DEFINE_bool(break_condition_truncate, true, "truncate value to 32-bits", "CPU");
DEFINE_bool(break_on_debugbreak, true, "int3 on JITed __debugbreak requests.",
"CPU");
+
+// AUDIT-DEMO: smoke marker (memory entry: emulator.cc:225,283). Always-on bool.
+DEFINE_bool(audit_demo_setup_trace, true,
+ "Audit smoke marker: log AUDIT-DEMO-SETUP-BEGIN at emulator setup.",
+ "Audit");
+
+// AUDIT-061: comma-separated list of guest PCs to log on each fire.
+// Format: "0xPC1,0xPC2,..." (max 32 PCs). Each fire emits
+// AUDIT-061-BR pc=X lr=X cr0=LGE cr6=LGE r3=X r4=X r5=X r6=X r31=X tid=N.
+// Default empty (off); no perf cost when empty.
+DEFINE_string(audit_61_branch_probe_pcs, "",
+ "AUDIT-061: CSV of guest PCs to trace (cr0/cr6 + regs/tid).",
+ "Audit");
+
+// AUDIT-067: comma-separated list of u32 values to watch. When non-empty,
+// every 4-byte guest store (stw/stwu/stwx/stwux/stmw) emits a runtime
+// equality check; matches log AUDIT-067-VAL pc=X lr=X val=X dst=X r3..r6 r31 tid=N.
+// Max 4 values. Default empty (off); zero overhead when empty.
+DEFINE_string(audit_67_value_watch, "",
+ "AUDIT-067: CSV of u32 values (max 4) — log every guest "
+ "store whose value matches.",
+ "Audit");
+
+// Phase A — see kernel/event_log.h.
+DEFINE_string(phase_a_event_log_path, "",
+ "Phase A: write schema-v1 JSONL event log to this path. "
+ "Empty (default) = disabled.",
+ "Audit");
+DEFINE_bool(phase_a_event_log_mem_writes, false,
+ "Phase A: include mem.write events in the JSONL log. RESERVED — "
+ "not wired in this phase. Default false.",
+ "Audit");
+
+// Phase B — see kernel/phase_b_snapshot.h.
+DEFINE_string(phase_b_snapshot_dir, "",
+ "Phase B: write 5-file structured state snapshot to "
+ "
/canary/ at the moment immediately before the first "
+ "guest PPC instruction of entry_point. Empty (default) = "
+ "disabled, zero overhead.",
+ "Audit");
+DEFINE_bool(phase_b_snapshot_and_exit, false,
+ "Phase B: after writing the snapshot, exit the process "
+ "immediately (std::_Exit(0)) so re-runs are byte-deterministic.",
+ "Audit");
+DEFINE_bool(phase_b_dump_section_content, false,
+ "Phase B: in memory.json, populate section_contents[].content_b64 "
+ "with raw bytes of every committed XEX-image region. Default "
+ "false — per-region SHA-256 is enough for the routine diff; "
+ "this is the escape hatch for the STOP-and-report condition "
+ "(image_loaded_sha256 mismatch).",
+ "Audit");
diff --git a/src/xenia/cpu/cpu_flags.h b/src/xenia/cpu/cpu_flags.h
index 38c4f98ba..5704a25c7 100644
--- a/src/xenia/cpu/cpu_flags.h
+++ b/src/xenia/cpu/cpu_flags.h
@@ -35,4 +35,32 @@ DECLARE_bool(break_condition_truncate);
DECLARE_bool(break_on_debugbreak);
+// AUDIT-DEMO smoke marker.
+DECLARE_bool(audit_demo_setup_trace);
+
+// AUDIT-061: multi-PC branch probe — emits one log line per fire with
+// (pc, lr, cr0 LGE, cr6 LGE, r3, r4, r5, r6, r31, tid). CSV of guest PCs.
+DECLARE_string(audit_61_branch_probe_pcs);
+
+// AUDIT-067: value-watch — emit a log line for each 32-bit guest store whose
+// value-to-be-stored matches any configured value. CSV of u32 values
+// ("0xDEADBEEF,..."), max 4 entries. Default empty (off); zero cost when empty.
+DECLARE_string(audit_67_value_watch);
+
+// Phase A: JSONL event-log emitter path. When non-empty, the engine writes
+// schema-v1 JSONL events to this file. Empty (default) = no overhead, no
+// behavior change. Schema: xenia-rs/audit-runs/phase-a-diff-harness/schema-v1.md
+DECLARE_string(phase_a_event_log_path);
+DECLARE_bool(phase_a_event_log_mem_writes);
+
+// Phase B: initial-state snapshot. When the dir cvar is non-empty, the
+// engine writes a five-file structured state snapshot (cpu_state.json,
+// memory.json, kernel.json, vfs.json, config.json, plus manifest.json) to
+// `/canary/` at the moment immediately before the first guest PPC
+// instruction of the XEX entry_point executes. See
+// `xenia-rs/audit-runs/phase-b-state-equivalence/`.
+DECLARE_string(phase_b_snapshot_dir);
+DECLARE_bool(phase_b_snapshot_and_exit);
+DECLARE_bool(phase_b_dump_section_content);
+
#endif // XENIA_CPU_CPU_FLAGS_H_
diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc
index cc7d90c2e..a8325a584 100644
--- a/src/xenia/kernel/xthread.cc
+++ b/src/xenia/kernel/xthread.cc
@@ -22,6 +22,7 @@
#include "xenia/cpu/processor.h"
#include "xenia/emulator.h"
#include "xenia/kernel/kernel_state.h"
+#include "xenia/kernel/phase_b_snapshot.h"
#include "xenia/kernel/user_module.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
@@ -575,6 +576,11 @@ void XThread::Execute() {
// On Windows, setjmp/longjmp is used because MSVC's longjmp performs SEH
// stack unwinding which already calls destructors.
uint32_t next_address;
+ // Phase B snapshot. No-op when phase_b_snapshot_dir cvar is empty
+ // (default). When set, fires once on the entry-point thread immediately
+ // before its first guest instruction executes. See
+ // xenia/kernel/phase_b_snapshot.h.
+ ::xe::kernel::phase_b::FireIfEntryThread(this, thread_state_, address);
#if !XE_PLATFORM_WIN32
try {
exit_code = static_cast(kernel_state()->processor()->Execute(
--- a/src/xenia/kernel/phase_b_snapshot.h (NEW FILE)
+++ b/src/xenia/kernel/phase_b_snapshot.h
@@ -0,0 +1,43 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Phase B initial-state snapshot. Cvar-gated (default off).
+ * Spec: xenia-rs/audit-runs/phase-b-state-equivalence/
+ ******************************************************************************
+ */
+
+#ifndef XENIA_KERNEL_PHASE_B_SNAPSHOT_H_
+#define XENIA_KERNEL_PHASE_B_SNAPSHOT_H_
+
+#include
+
+namespace xe {
+namespace cpu {
+class ThreadState;
+} // namespace cpu
+namespace kernel {
+
+class XThread;
+
+namespace phase_b {
+
+// Called immediately before the JIT executes the first guest PPC
+// instruction of a thread. Returns silently when:
+// * phase_b_snapshot_dir cvar is empty (zero overhead — default off);
+// * a snapshot has already been written (one-shot CAS guard);
+// * `entry_address` does not match the loaded executable module's
+// entry_point (this thread is not the entry thread — a worker
+// spawned by an early kernel call could reach its first instruction
+// before the boot thread does).
+//
+// On a match: writes /canary/{cpu_state,memory,kernel,vfs,config}.json
+// + manifest.json, optionally `_Exit(0)` per phase_b_snapshot_and_exit.
+void FireIfEntryThread(XThread* xthread, cpu::ThreadState* thread_state,
+ uint32_t entry_address);
+
+} // namespace phase_b
+} // namespace kernel
+} // namespace xe
+
+#endif // XENIA_KERNEL_PHASE_B_SNAPSHOT_H_
--- a/src/xenia/kernel/phase_b_snapshot.cc (NEW FILE)
+++ b/src/xenia/kernel/phase_b_snapshot.cc
@@ -0,0 +1,899 @@
+/**
+ ******************************************************************************
+ * Xenia : Xbox 360 Emulator Research Project *
+ ******************************************************************************
+ * Phase B initial-state snapshot. See phase_b_snapshot.h.
+ ******************************************************************************
+ */
+
+#include "xenia/kernel/phase_b_snapshot.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include