# diff-state Phase B initial-state snapshot diff tool. Stdlib-only Python. Mirrors the shape of `tools/diff-events/` but operates on the *static structural* snapshots emitted by `phase_b_snapshot` at the moment immediately before the first guest PPC instruction of the XEX entry_point executes. ## Usage ```bash python3 tools/diff-state/diff_state.py \ --canary /canary \ --ours /ours \ --out /report.md ``` Writes: - `/report.md` — human-readable divergence catalog - `/report.json` — machine-readable sibling (same content) ## Exit codes | code | meaning | |---|---| | 0 | no divergence (or `--validate-identical` succeeded) | | 1 | divergences found | | 2 | STOP triggered (`image_loaded_sha256` / `xex_entry_point` / `iso_sha256` mismatch) | ## Field-comparison rules Lives at the top of `diff_state.py` as Python constants — read those for the authoritative spec. Summary: - `engine`, `schema_version`, `deterministic_skip` are always skipped. - `cpu_state.json`: skip `hw_id`. - `kernel.json`: skip `raw_handle_id`, `exports_registered_count`. - `config.json`: skip `build_id`, `iso_path`, `host_ns_at_snapshot`, `wall_clock_iso8601`, `cli_argv`, `cvars.phase_b_snapshot_dir`. - Each snapshot's `deterministic_skip` array is honored too. ## Set vs sequence semantics - **Set** (sort by key, then positional compare): - `kernel.json::objects` (key=`handle_semantic_id`) - `kernel.json::handle_name_table` (key=`name`) - `vfs.json::cache_root_listing` (key=`relpath`) - `memory.json::heaps` (key=`base`) - **Sequence** (positional compare): everything else, including `memory.json::regions` (which both engines emit pre-sorted by `(start, end)`). ## Classification | class | trigger | priority | |---|---|---| | σ-structural | field missing/extra; sequence-length mismatch; set element only in one engine | 1 (always report) | | δ-content-STOP | `image_loaded_sha256` / `xex_entry_point` / `iso_sha256` mismatch | STOP (exit 2) | | δ-content | other `*_sha256` field differs | 2 | | γ-kernel-content | `objects[].details` field differs | 2 — primary Phase C target | | κ-cache | non-empty `cache_root_listing` either side | re-run after `rm -rf` of caches | | ε-host-allocator | heap base/region start differs but sha256 agrees | catalog only | | τ-host-timing | `deterministic_skip`-listed timing field | silent unless verbose | ## Negative-test recipe To verify the tool catches a hand-mutation: ```bash cp -r snap-001/ours snap-001/ours-mut sed -i 's/"thread_id": 1/"thread_id": 999/' snap-001/ours-mut/kernel.json python3 tools/diff-state/diff_state.py \ --canary snap-001/ours --ours snap-001/ours-mut --out /tmp/r.md # exit code 1; report names objects[handle_semantic_id=...] details.thread_id ```