# Observability surface — the parity contract > Companion to [`NORTH_STAR.md`](./NORTH_STAR.md) and [`SPEC.md`](./SPEC.md). > NORTH_STAR establishes that the simulator must be **observably > equivalent** to production: every internal observation a process > makes — retry counts, peer-state distributions, timer firings, > transport stats, queue depths, tails as well as means — must be > statistically indistinguishable from the corresponding observation > in a real deployment under matched conditions. This document > enumerates the surface against which "matched" is measured, both > what is recorded today and what is in the near pipeline. The > simulator's SPEC ties its obligations to this enumeration; if > something in here is unimplemented in the sim, the sim is > incomplete; if something is unimplemented in production recording, > the recording is incomplete. Extending the two is the same project. ## Table of contents 1. Why this doc exists 2. How to read it 3. Surface today 3.1 Identity 3.2 Per-peer reachability log 3.3 Structured event stream 3.4 Snapshot envelope 3.5 Clock-alignment samples 3.6 Tier-2 iroh introspection 3.7 Tier-2 SWIM introspection 3.8 Tier-3 host / DNS state 3.9 Tier-3 outbound probes 3.10 Tier-3 vast.ai context 3.11 Tier-3 process resource stats 3.12 Bundle layout 4. Surface in the near pipeline 4.1 Wire-level packet trace 4.2 Causal trace IDs 4.3 Per-flow congestion-control state 4.4 Application observables (pipeline-parallel) 4.5 Per-actor mailbox and scheduler state 4.6 NAT-binding lifecycle 4.7 Cryptographic handshake records 4.8 Per-link bandwidth and loss telemetry 5. Out of scope (not in the floor) 6. Parity rules of the road --- ## 1. Why this doc exists The simulator's job is not to imitate a network. It is to be indistinguishable from one across whatever observation channels the process has. The channels are not abstract: they are the records the diagnostics stack writes to disk. As long as those records are the only thing downstream tooling consumes, "the sim is faithful" reduces to "the sim emits the same records, with the same distributions, as production does." Three properties follow: - **The recording schema is the contract.** Adding an observation channel in production without simultaneously committing the sim to emit it is a parity failure — by NORTH_STAR §3, that asymmetry is always a bug. So every record kind enumerated here is either (a) already produced by both, or (b) committed to be produced by both in the same change. - **Distributions, not just shapes.** Schema parity is necessary, not sufficient. The sim must reproduce the *tails* of every numeric field — latencies, retry counts, packet sizes — to within the noise floor of the real measurement. A schema match that papers over a tail-shape mismatch is a parity failure. - **No back-channel introspection.** A sim-only diagnostic (queue-depth-inside-the-sim, deterministic-RNG-trace) is fine and useful, but must not be relied on by any code that also runs in production. The node-under-test must not be able to tell which runtime it is on. Sim-only diagnostics live next to, not inside, the production recording surface. ## 2. How to read it Each subsection below describes *one record kind*. For each: - **Source of truth** — the type or file in `crates/distribution` (today) or the location the future implementation will land. - **Trigger** — when the record is emitted (boot, periodic, event- driven, on-demand). - **Fields** — the observable signal carried. The level of detail matches what the post-processor (or any future analyzer) reads. - **Sim obligation** — the specific surface the sim must reproduce for this record, including which fields require *distributional* parity vs. mere presence. Section 3 covers what is in the wire today; the schemas there are authoritative against the source files cited. Section 4 covers what is committed but not yet landed; the schemas there are indicative — the source files settle final field names. --- ## 3. Surface today The numbered subsections below mirror the tiers from `examples/pipeline-parallel-inference/DIAGNOSTICS_PLAN.md`. Each is implemented in `crates/distribution/src/diagnostics/*` and the record types are stable enough to bind the sim to. ### 3.1 Identity **Source of truth.** `diagnostics::identity::Identity`. Re-emitted in the header of every snapshot. **Trigger.** Boot; re-embedded into every subsequent record so any single record in the bundle is interpretable on its own. **Fields.** `node_id_hex`, `node_id_short`, `role`, `stage_index`, `stage_count`, `run_id`, `vastai_contract_id`, `host_ip_public`, `host_country`, `datacenter_id`, `hostname`, `container_id`, `process_start_unix_ms`, `boot_sequence`, `binary_version`, `git_sha`, `iroh_version`, `home_relay_url_at_boot`. **Sim obligation.** Every sim-hosted node must construct an `Identity` with the same shape. `node_id` is generated by the sim (deterministically, from seed + name). Environmental fields (`vastai_contract_id`, `host_country`, `datacenter_id`, `host_ip_public`) take values from the sim's topology spec rather than the real metadata — they are *parameters* of the run, not captured observations, but appear in the same field. `git_sha` and `binary_version` carry the same values they would in prod (the sim does not stub them out). ### 3.2 Per-peer reachability log **Source of truth.** `diagnostics::reachability::PeerReachability`. One entry per remote peer, maintained continuously in the aggregator and emitted as part of every snapshot. **Trigger.** In-memory, continuous. Serialized at every snapshot. **Fields.** `peer_node_id_hex`, `last_inbound_packet_at_ms` + `via_relay_url`, `last_outbound_success_at_ms` + `via_relay_url`, `last_dial_started_at_ms`, `last_dial_outcome`, `last_dial_duration_ms`, `current_swim_opinion`, `current_swim_opinion_since_ms`, `swim_transition_history` (bounded ring of ~32 `StateTransition`), `metadata_version_seen`, `metadata_relay_url_seen`. **Sim obligation.** The sim's transport layer must drive the same log. Distributional parity required on `last_dial_duration_ms` (must match the per-link RTT model); `current_swim_opinion_since_ms` (must follow the same suspect timer dynamics); the rate of `swim_transition_history` entries under partition and heal. ### 3.3 Structured event stream **Source of truth.** `diagnostics::event::Event` and `EventRecord`. Append-only stream batched to the collector every ~1s and immediately on state transitions. **Trigger.** Event-driven. Each variant is fired by the subsystem that observes the event (transport for `Dial*` / `Message*` / `ConnectionCache*` / `IrohConnTypeChanged` / `RelayChanged` / `NodeMapUpdate`; SWIM for `SwimTransition` / `SwimMetadata*`; probe scheduler for `Probe*`; any subsystem for `Error` / `Custom`). **Variants (minimum set).** | Variant | Fired by | Carries | |---|---|---| | `SwimTransition` | SWIM | `peer`, `from`, `to`, `reason` | | `DialStarted` | transport | `peer`, `attempt`, `timeout_ms` | | `DialOutcome` | transport | `peer`, `attempt`, `outcome`, `duration_ms` | | `IrohConnTypeChanged` | transport | `peer`, `old`, `new` | | `RelayChanged` | transport | `old_url`, `new_url` | | `SwimMetadataSent` / `Received` | SWIM | `version`, `payload_hash` (+ `peer` on Received) | | `ConnectionCacheHit` / `Miss` / `Invalidated` | transport | `peer`, `generation`, `reason?` | | `NodeMapUpdate` | transport | `peer`, `from_source`, `accepted` | | `MessageSent` / `Received` | transport | `peer`, `kind`, `size` | | `ProbeSent` / `Received` | probes | `target`, `kind`, `rtt_ms?`, `outcome` | | `Error` | any | `component`, `message`, `peer?` | | `Custom` | any | `kind`, `fields` (arbitrary JSON) | The wrapping envelope (`EventRecord`) carries `node_id`, `monotonic_seq` (per-process, never decreases), and best-effort `wall_ms`. **Sim obligation.** Every variant fires from the sim-side transport and SWIM implementations at the same points it fires in prod. Distributional parity required on: - `DialOutcome.duration_ms` per outcome class. - `ConnectionCacheInvalidated` rate per session. - `MessageSent` / `MessageReceived` size and inter-arrival distributions per `kind`. - `SwimTransition` cadence under each modelled fault scenario. `monotonic_seq` is the sim-internal causal order; `wall_ms` is populated from virtual time and post-aligned via §3.5 just as in prod. ### 3.4 Snapshot envelope **Source of truth.** `diagnostics::snapshot::Snapshot` / `SnapshotBody` / `SnapshotTrigger`. **Trigger.** Periodic (default 5s), on local SWIM transition (`SnapshotTrigger::Transition`), or on collector hint (`SnapshotTrigger::OnDemand`). **Fields.** Identity (re-embedded), `run_id`, `snapshot_id`, `wall_ms`, `monotonic_seq`, `trigger`, and a `SnapshotBody` with `reachability` (always), an `events` tail since the last snapshot, and `Option` blocks for `iroh` / `swim` / `host` / `probes` / `vastai` / `process` (populated if the corresponding introspector is installed). **Sim obligation.** Sim-hosted nodes emit `Snapshot` records on the same triggers. The periodic cadence is a *sim parameter* — the sim honors whatever the run config sets, just as prod honors whatever its periodic config sets. The `SnapshotTrigger` distribution (what fraction of snapshots come from periodic vs. transition vs. on-demand) must match the real-run distribution for the matched scenario. ### 3.5 Clock-alignment samples **Source of truth.** Sink handshake — every POST carries `node_send_ms`; the response carries `{node_send_ms_echoed, collector_recv_ms, collector_send_ms}`; the node records its `wall_ms_at_receive`. Each round is logged as a structured event so the post-processor has a per-node offset stream over the run's lifetime, not a one-shot calibration. **Trigger.** Every collector POST. **Sim obligation.** The sim hosts a sim-side collector; its "wall clock" is virtual time. Sim runs emit `clock_sample` events with the same shape so the post-processor's alignment code is unchanged across sim and prod. In sim, alignment error is zero by construction — that *is* the answer the parity check expects, not a sign of missing telemetry. ### 3.6 Tier-2 iroh introspection **Source of truth.** `diagnostics::snapshot::Tier2IrohState`, populated by `IrohIntrospector` (production implementation: `diagnostics::iroh_introspect::IrohIntrospect`). **Trigger.** Captured on every snapshot when an introspector is installed. **Fields per peer (`Tier2Peer`).** `peer_node_id_hex`, `conn_type` (Direct / Relay / Mixed / None), `latency_ms`, `last_used_ms`, `last_received_ms`, `direct_addresses` (each `TransportAddrWire{addr, usage}`), `relay_urls`, `addr_sources`. **Top-level fields.** `home_relay_url`, `peers`, `metrics` (`Vec` — flat dump of `iroh-metrics` counters / gauges / histograms), `connection_cache` (per-peer `Tier2ConnectionCache`: `generation`, `created_at_ms`, `last_successful_send_at_ms`, `last_failure_at_ms`, `last_failure_reason`, `observed_conn_type_at_last_use`), `api_gaps`, `scraped_at_ms`. **Sim obligation.** The sim's iroh-equivalent transport produces a `Tier2IrohState` with the same field set populated. Where the real iroh exposes nothing (today: `latency_ms`, `last_used_ms`, `last_received_ms`, `addr_sources`), the sim *also* leaves those `None` and lists them in `api_gaps`. Distributional parity required on `conn_type` evolution per peer (the rate of Direct ↔ Relay flips under NAT churn), and on the deltas of every metric in `metrics` across the run. ### 3.7 Tier-2 SWIM introspection **Source of truth.** `diagnostics::snapshot::Tier2SwimState`, populated by `SwimIntrospect`. **Fields.** `config` (`Tier2SwimConfig`: probe / probe-timeout / suspicion-timeout / dead-reprobe-interval ticks, indirect-probes-k, gossip fanout Λ, max-piggyback, probe-mode string), `self_node_id_hex`, `self_incarnation`, `metadata_local_version`, `peers` (per-peer `Tier2SwimPeer`: state, incarnation, last_ping_sent / last_ack_received / last_ping_received / suspect_started timestamps, `metadata_version_seen`), `recent_messages` (bounded ring of ~64 `Tier2SwimMessage`: `{kind, peer, at_ms, sequence?}`), `scraped_at_ms`. **Sim obligation.** The simulated SWIM emits this snapshot block with `Tier2SwimConfig` echoing the configured parameters of the sim run and `peers` reflecting the simulator's authoritative membership view. The `recent_messages` ring matches the same cap and ordering rule (oldest first). Distributional parity required on suspect-timer durations and incarnation churn under modelled fault scenarios. ### 3.8 Tier-3 host / DNS state **Source of truth.** `Tier3HostState` / `Tier3HostNetwork` / `Tier3DnsResolution`, populated by `HostIntrospect` at boot and on ~30s refresh. **Host-network fields.** `interfaces` (`name`, `addresses`, `mtu`, `up`), `default_routes` (`family`, `destination`, `gateway`, `interface`), `udp_sockets` (rows from `/proc/net/udp{,6}`: `local_addr`, `remote_addr`, `state`, `inode`), `conntrack_count`, `ipv6_enabled`, `resolv_conf_nameservers`, `refreshed_at_ms`. **DNS fields.** Per known relay URL: `hostname`, `a_records`, `aaaa_records`, `ttl_seconds`, `resolver_used`, `resolved_at_ms`, `error`. **Sim obligation.** Sim-hosted nodes emit a `Tier3HostState` populated from the simulated host's modelled environment: virtual interfaces (with whatever `mtu` / `up` the topology spec assigns), modelled default routes, the modelled UDP-socket table (every `bind()` the sim drives produces a row), modelled DNS results (controlled by the sim's DNS model — see §4.6 in SPEC). Fields the sim does not model (e.g. `conntrack_count` if the conntrack model is not yet in) follow the prod convention: emit `None` and log a single `Error` event noting the gap. ### 3.9 Tier-3 outbound probes **Source of truth.** `Tier3ProbeState` / `Tier3Probe`, populated by `ProbeScheduler`. **Fields.** Per target: `target`, `kind` (`"udp_echo"`, `"udp_relay"`, `"stun"`), `resolved_addr`, `last_attempted_at_ms`, `last_outcome` (`"ok"`, `"timeout"`, `"refused"`, `"error"`, `"unresolved"`), `last_rtt_ms`, `last_error`, `attempts`, `successes`. **Sim obligation.** The sim drives the probe scheduler exactly the way prod does — registering targets at boot and letting the scheduler run on its ~10s cadence. The sim's UDP stack accepts / drops / delays probe packets according to its link physics, and the resulting `Tier3Probe` block must show the same outcome distribution per modelled link condition. ### 3.10 Tier-3 vast.ai context **Source of truth.** `Tier3VastaiContext`, captured once at boot by `VastaiContext::capture_now()`. **Fields.** `container_id`, `hostname`, `env_vars` (filtered set of `VAST_*` / `VASTAI_*` / `CONTAINER_*` / `CUDA_*` / `NVIDIA_*`, with secret-bearing keys dropped), `process_start_ms`, `captured_at_ms`. **Sim obligation.** In sim runs the block is populated with the *modelled* vast.ai context — whatever the topology spec assigns each node, with the same key prefixes. If the sim run is not modelling a vast.ai-style environment, the block is absent; this matches the prod convention (orchestrator on a laptop, no container, no block). ### 3.11 Tier-3 process resource stats **Source of truth.** `Tier3ProcessStats`, populated by `ProcessStats`. **Fields.** `rss_bytes`, `vm_size_bytes`, `open_fd_count`, `cpu_ms`, `tokio` (`Tier3TokioStats { flavor }`), `captured_at_ms`. **Sim obligation.** The sim cannot directly produce RSS / VmSize the way `/proc/self/status` does, because the sim hosts many nodes in one OS process. Field-level approximation: the sim populates these fields from its modelled per-node accounting (see SPEC §4.8 "host budget"). Where there is no model (e.g. open_fd_count is not modelled today), the field is `None`, matching the "absent ≠ zero" rule. ### 3.12 Bundle layout **Source of truth.** Collector binary (`diagnostics::collector::*`) and bundle assembler (`postproc::tar`). See `DIAGNOSTICS_PLAN.md` §"Bundle Format" for the on-disk shape. **Sim obligation.** A sim run produces a bundle byte-for-byte indistinguishable from a prod bundle of the same workload, given the same `run_id`. The bundle is the artifact the calibration loop diffs against. --- ## 4. Surface in the near pipeline These items are not in the wire today but are within reach of the next round of recording work. Each is listed so the sim's SPEC can commit to producing it the moment prod recording lands it (and vice versa — committing here means the sim is not allowed to lag once production starts emitting). ### 4.1 Wire-level packet trace **Need.** Today's records describe message-level activity (`MessageSent` / `MessageReceived` count and size by `kind`). That granularity hides protocol-internal behavior: QUIC frame fragmentation, ACK timing, retransmits, MTU-driven path-MTU probes. Calibration runs hit a ceiling: when the sim's `MessageReceived` distribution matches prod's, but flow control inside QUIC behaves differently, the discrepancy is invisible. **Shape (proposed).** A `WirePacket` record stream: ``` { node_id, monotonic_seq, wall_ms, direction: "tx" | "rx", l3: { src_ip, dst_ip, ttl, ecn }, l4: { kind: "udp", src_port, dst_port, len }, payload_hash: u64, payload_len: u32, via: { local_iface, peer_relay_url? } } ``` Payloads themselves are not stored by default; `payload_hash` suffices for stitching a sim run against a prod run for the same workload. A "full capture" mode (gated, off by default) keeps the bytes for offline diffing. **Sim obligation.** The sim emits a `WirePacket` per modelled packet event. Distributional parity required on inter-packet gap per `(src_port, dst_port)` flow. ### 4.2 Causal trace IDs **Need.** Stitching is currently done by clock alignment + per-peer reachability matching. That works for "did A go dead before B's dial timed out" but fails for richer causality (e.g., "this SWIM-piggybacked metadata update is the one that triggered the NodeMapUpdate seven hops later"). A trace ID threaded through each message makes causality direct. **Shape (proposed).** Every `MessageSent` records a `trace_id` (opaque 128-bit) and a `parent_trace_id`. Each `MessageReceived` echoes the same `trace_id`. Subsystems that derive new work from a received message (SWIM digest → SwimMetadataSent, transport NodeMapUpdate, etc.) thread `trace_id` into their own emitted events as `parent_trace_id`. **Sim obligation.** The sim threads the same trace IDs through its transport and SWIM implementations. Determinism extends to the IDs: same seed, same trace IDs (derived from the in-process deterministic RNG; see SPEC §2.1). ### 4.3 Per-flow congestion-control state **Need.** Network-condition parity demands more than RTT and loss rate — the *response* to those conditions (cwnd evolution, retransmit timer, RTT estimator) is part of what production observes (and so must be part of what the sim observes). Today we infer congestion behavior from message size and timing; we should record it directly. **Shape (proposed).** Inside `Tier2IrohState`, a new `flows` array: ``` { peer_node_id_hex, local_addr, remote_addr, cwnd_packets, rtt_estimate_ms, rtt_variance_ms, bytes_in_flight, retransmits_total, packet_loss_rate_recent } ``` **Sim obligation.** The sim's QUIC implementation exposes the same state. Distributional parity required on `cwnd_packets` and `rtt_estimate_ms` evolution under each modelled link condition; on the cumulative `retransmits_total` per minute of run. ### 4.4 Application observables (pipeline-parallel) **Need.** The diagnostics layer captures everything *under* the application. Calibration of an inference deployment also needs the application-level surface: per-stage activation transfer timing, KV-cache pressure, per-token latency, worker queue depths. Today these are local logs at best. **Shape (proposed).** A new `Custom` event family with well-known `kind` values, plus a tier-3 application block: | Event `kind` | Carries | |---|---| | `pp.token_emitted` | `request_id`, `position`, `token_id`, `decode_ms` | | `pp.activation_sent` | `request_id`, `from_stage`, `to_stage`, `bytes`, `is_prefill` | | `pp.activation_received` | mirror of `_sent` | | `pp.worker_dispatch` | `op`, `request_id`, `queue_depth_at_dispatch` | | `pp.worker_complete` | `op`, `request_id`, `duration_ms` | Plus a `Tier3AppState` block in `SnapshotBody`: ``` { stage_index, worker_status, in_flight_requests: u32, kv_cache_blocks_used: u32, kv_cache_blocks_max: u32, worker_queue_depth: u32, gpu_mem_used_bytes?, gpu_mem_total_bytes? } ``` **Sim obligation.** Sim-native pipeline stages drive these events through the same crate. Where the sim does not model the GPU (today's case), `gpu_mem_*` is `None` and the gap is logged once. Distributional parity required on `decode_ms` and `bytes` distributions per `from_stage→to_stage` link. ### 4.5 Per-actor mailbox and scheduler state **Need.** Tokio-runtime stats today report only the runtime flavor (stable surface). Inside the actor framework, mailbox depth and per-actor latency are observable but uncaptured. Pipeline stalls caused by mailbox backpressure or executor starvation look identical to network failures from outside. **Shape (proposed).** A `Tier3ActorState` block: ``` { actors: [{ name, mailbox_depth, oldest_msg_age_ms, total_msgs_processed, total_msgs_dropped, p50_process_ms, p99_process_ms }], scheduler: { task_count, oldest_pending_task_age_ms } } ``` **Sim obligation.** The sim-side actor runtime exposes the same counters per actor. Distributional parity required on `mailbox_depth` time series and on `p99_process_ms` under load. ### 4.6 NAT-binding lifecycle **Need.** "Asymmetric routing" and "relay flapped" today get diagnosed by stitching `RelayChanged`, reachability log, and host scrape together. A direct NAT-binding observation would collapse that to one record. Even without conntrack on every host, the path-discovery layer in iroh has visibility into when a binding was last refreshed. **Shape (proposed).** Inside `Tier2IrohState`, a new `nat_bindings` array: ``` { local_addr, observed_remote_addr, first_seen_ms, last_refreshed_ms, refresh_count, keepalive_interval_ms } ``` **Sim obligation.** The sim's NAT model produces the same record. Distributional parity required on `last_refreshed_ms` interval under each modelled NAT class (cone / symmetric / pmp / upnp). ### 4.7 Cryptographic handshake records **Need.** Handshake-level observations are part of the wire today (MagicSock holepunch + QUIC TLS); they show up in metrics as counter increments only. A direct record of each handshake makes connection-establishment bugs first-class. **Shape (proposed).** A new event family: ``` HandshakeStarted { peer, role: "client" | "server", local_addr, remote_addr, via_relay?, cipher_suite_offered } HandshakeOutcome { peer, outcome: "success" | "timeout" | "rejected" | "error(string)", duration_ms, cipher_suite_selected? } ``` **Sim obligation.** The sim's transport emits both events on every modelled handshake. The set of `cipher_suite_offered` and the `cipher_suite_selected` distribution match what the real transport would. ### 4.8 Per-link bandwidth and loss telemetry **Need.** The sim's link physics (SPEC §4.2) is parameterized; production today does not record link-level bandwidth or loss directly. The path-forward is symmetric: the sim records its *applied* link parameters as a snapshot field (so a replay knows what the link was set to), and prod adds passive estimation of the same parameters from observed flow behavior. **Shape (proposed).** Inside `Tier2IrohState`, a `links` block: ``` { peer_node_id_hex, bandwidth_bps_observed: u64, loss_rate_observed: f32, one_way_delay_ms_estimated: u64, measurement_window_ms: u64 } ``` For sim runs only, an additional `links_applied` block records the configured ground truth — used by the post-processor to quantify the gap between *applied* (sim) and *observed* (sim or prod) link parameters. **Sim obligation.** Drive the `links` block from the same estimation code prod uses, plus the `links_applied` block from the topology spec. Calibration validates that `bandwidth_bps_observed` converges to `links_applied.bandwidth` within the sim, and that prod-side estimation tracks it on real deployments. --- ## 5. Out of scope (not in the floor) The following are explicitly **not** part of the parity surface in the current planning horizon. The sim is not obligated to reproduce them; production is not obligated to record them. If that changes, this list moves into §4 first, then into §3. - Kernel-level system call traces (eBPF / strace). - Allocator events (per-allocation timing, heap fragmentation). - Dynamic linker / LD_PRELOAD-style observations. - PTP / GPS clock sync internals. - GPU compute-kernel internals beyond aggregate memory usage. - CPU performance counters (cache misses, branch prediction). - Per-thread scheduler decisions below the tokio-task level. The line is drawn at "what an unprivileged userspace process can observe about itself or the network it touches." Anything below that line is outside the model floor today. --- ## 6. Parity rules of the road 1. **No silent gaps.** Every record kind in §3 is either populated or explicitly marked absent (`None` + an `Error` event noting the gap). A missing field that the reader cannot distinguish from "the value happens to be zero" is a parity bug. 2. **Schema match is the floor, distribution match is the bar.** The presence of a field is necessary but not sufficient. Each subsection above calls out which fields require *distributional* parity (matching tails, not just means) under matched scenarios. 3. **Symmetry between recording and simulation.** Both directions are bugs. A field prod records that the sim cannot emit is a sim incompleteness. A field the sim emits that prod cannot record is a recording incompleteness. The fix is always to extend both together — never to drop a field from one to restore symmetry. 4. **Determinism is the sim's superpower.** The sim is allowed (and expected) to expose *additional* records that have no prod equivalent — RNG traces, scheduler decision logs, exact virtual-time tick streams. These are sim-only diagnostics, never visible to the node-under-test, and never required for prod parity. They live in a separate tier (sim §8.2 in SPEC) so the parity check does not have to reason about their absence in prod runs. 5. **Calibration is the judge.** Whether §3 and §4 are correctly implemented is not decided in this doc. It is decided by the calibration loop (SPEC §9): deploy, collect the bundle, replay in sim, diff every record kind enumerated above. Divergence beyond the noise floor is a bug in this doc, in the sim, or in the recording — exactly one of the three.