Relay/subprocess/registry introspectors, build-time dep-version + git-sha, postproc RTT/inference render, new swactor-iroh-relay binary; dashboard replay-viewer, sim SWIM tuning report + repro tests, N3 postmortem docs. Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
561 lines
24 KiB
Markdown
561 lines
24 KiB
Markdown
# Simulator hardening — behavioral spec
|
||
|
||
Sister doc to `N3_OBSERVABILITY_UPGRADE_SPEC.md` and `SIM_SPEC.md`. The
|
||
observability spec says *what the bundle must contain after a real or
|
||
simulated run*. The sim spec says *what the simulator's MVP must do*.
|
||
This doc says *what the simulator must do beyond the MVP to be a credible
|
||
pre-deployment gate* — the behavior that closes the loop "we keep
|
||
deploying to vast.ai, finding one bug, fixing it, and finding the next
|
||
one in the next deploy."
|
||
|
||
Throughout: every contract is testable. A simulator that does not
|
||
satisfy these may still be useful for hand-written reproductions, but
|
||
it does not earn the right to block or unblock a deployment.
|
||
|
||
## 0. Motivation
|
||
|
||
Eight live N≥3 deploys have produced eight distinct failure modes.
|
||
Each one has been caught only by spending GPU rental, waiting 45–90
|
||
minutes for the cluster to come up, and reading the bundle after the
|
||
fact. The fix lands. The next deploy surfaces the next bug. The sim,
|
||
in its current form, has not preempted any of these failures — it
|
||
reproduces them after we know what to look for.
|
||
|
||
The gap is not that the simulator is wrong. It is that the simulator
|
||
is *narrow*. It exercises one host kind (SWIM), one transport model
|
||
(direct or one-relay), one fault dimension at a time, and one scenario
|
||
per fault. Production exercises three host kinds, two transports
|
||
stacked, multiple faults stacked, and a continuous distribution of
|
||
timing and size. The bugs live in the cross-product the sim doesn't
|
||
visit.
|
||
|
||
We are not running a database. We do not need 10^10 simulated years.
|
||
We need to *extrapolate heuristically from known failure shapes* —
|
||
treat each postmortem as the seed of a family of scenarios, and let
|
||
the sim explore the family densely while ignoring the rest of the
|
||
state space.
|
||
|
||
## Cross-cutting requirements
|
||
|
||
1. **Same code, sim and prod.** Every actor whose behavior matters
|
||
for a known failure mode runs the same source in the sim as in
|
||
prod. The sim wraps the actor in an adapter that routes its time,
|
||
randomness, and I/O through the engine; it does not reimplement
|
||
the actor's logic. A bug fix that lands in the actor lands in the
|
||
sim automatically, with no separate sim-side change.
|
||
|
||
2. **Determinism from `(scenario, seed)`.** Every run is fully
|
||
reproducible from the scenario file and the engine seed. Two runs
|
||
of the same `(scenario, seed)` produce byte-identical bundles. A
|
||
bug surfaced by the fuzzer is replayable by a developer with a
|
||
single command and the printed seed.
|
||
|
||
3. **Heuristic over exhaustive.** The sim does not attempt to enumerate
|
||
reachable states. It samples densely around shapes that have
|
||
already broken in production and shapes that are structurally
|
||
analogous to those. The unit of effort is "explore the
|
||
neighborhood of one postmortem," not "explore the system."
|
||
|
||
4. **Failure surfaces at the moment of violation.** When an invariant
|
||
is broken, the run halts at the violating step, not at end-of-run.
|
||
The bundle records which invariant failed, the virtual time it
|
||
failed at, and the state of every host at that instant. A
|
||
developer reading the bundle never has to scroll backwards from a
|
||
downstream symptom to find the originating event.
|
||
|
||
5. **Bundle-shape parity with prod.** A bundle produced by the sim is
|
||
shape-identical to a bundle produced by a real deploy: same
|
||
manifest schema, same event kinds, same snapshot fields, same
|
||
post-processor output. A reader cannot tell sim from prod from
|
||
data alone. (This requirement is shared with the observability
|
||
spec's section "Sim cross-pollination.")
|
||
|
||
6. **Sub-second iteration.** A single sim run of a 3-node scenario,
|
||
including bundle assembly and invariant evaluation, completes in
|
||
under one second on the developer's machine. A failing seed found
|
||
by the fuzzer replays in under one second too. This is what makes
|
||
"extrapolate from a postmortem" cheap enough to do every time.
|
||
|
||
7. **What this is not.** Not a model checker. Not a proof of
|
||
correctness. Not a replacement for staging deploys. Not a
|
||
guarantee of zero bugs in prod. The sim is a high-bandwidth filter
|
||
between "developer believes the change is correct" and "developer
|
||
has paid two dollars and forty-five minutes to find out."
|
||
|
||
---
|
||
|
||
## 1. Production code path coverage
|
||
|
||
After this work, every actor whose misbehavior produced a known
|
||
production failure runs inside the sim engine, wrapped in a host
|
||
adapter, with its time / randomness / I/O routed through the engine.
|
||
|
||
The minimum set is:
|
||
|
||
- The SWIM state machine (already present).
|
||
- The iroh driver, including its relay-session state machine and its
|
||
per-peer connection cache.
|
||
- The subprocess driver (`swactor_process` or its successor),
|
||
including spawn, exit, signal delivery, and stdout/stderr capture.
|
||
- The pipeline stage supervisor lifecycle — the actor that owns "is
|
||
my worker up, did it emit `worker_ready`, did it die for an
|
||
internal reason."
|
||
- The orchestrator-side actor that consumes membership updates and
|
||
decides whether the cluster is ready to accept inference.
|
||
|
||
A node simulated by the engine is a composition of these host
|
||
adapters, wired to a single virtual clock, RNG, and network. A
|
||
scenario that names "node X runs the orchestrator role" instantiates
|
||
all four adapters for node X; a scenario that names "node Y runs a
|
||
stage" instantiates the stage subset.
|
||
|
||
When the production code for one of these actors changes, the sim
|
||
host kind for it does not need to be edited. The adapter is a thin
|
||
shim over the production trait surface; rebuilding the sim with the
|
||
new actor source is the only update required.
|
||
|
||
Acceptance: a scenario that boots three nodes (one orchestrator, two
|
||
stages), advances the virtual clock until SWIM converges, and
|
||
inspects the resulting bundle, exercises the same `iroh_driver.rs`,
|
||
`stage_actor.rs`, and SWIM code paths that a live `pp-smoke-run`
|
||
exercises. Code coverage measured on the sim run matches code
|
||
coverage measured on a live run to within a stated tolerance, with
|
||
the gap attributable to OS-call-site stubs only.
|
||
|
||
---
|
||
|
||
## 2. Fault catalog
|
||
|
||
After this work, every fault the sim can inject is a value of a
|
||
closed enum. A scenario expresses its fault sequence as a list of
|
||
those values plus their timing; the fuzzer composes new sequences
|
||
from the same enum.
|
||
|
||
The enum's variants cover, at minimum, the dimensions production has
|
||
already hit and the dimensions adjacent to them. Not exhaustive of
|
||
all possible faults — exhaustive of the failure classes the
|
||
postmortems and the observability spec name. Concretely:
|
||
|
||
- **Network-level**: drop a packet, delay a packet by a duration
|
||
drawn from a distribution, partition (symmetric or asymmetric)
|
||
between two host subsets, reorder a packet relative to others on
|
||
the same link, duplicate a packet, cap a link's bandwidth, jitter
|
||
link latency around a baseline.
|
||
- **Relay-level**: close a relay session for a named reason at a
|
||
named time, evict the relay's session for a peer when the relay's
|
||
per-peer queue exceeds a size, drop one relay's tunnel to one peer
|
||
while leaving its tunnel to others intact (the 2026-05-25 shape),
|
||
flap a relay session repeatedly within a window.
|
||
- **Subprocess-level**: refuse a spawn, spawn-and-immediately-exit
|
||
with a named exit code, spawn-and-stall-before-protocol-output,
|
||
exit mid-run with a named signal, OOM-kill the subprocess at a
|
||
named time, slow the subprocess's response loop by a factor.
|
||
- **Clock-level**: skew one node's clock by a duration, drift one
|
||
node's clock at a rate, freeze one node's clock for a window.
|
||
- **Host-environment-level**: rebind the node's NAT mapping mid-run,
|
||
change the node's apparent public IP, simulate a transient
|
||
unreachable network namespace, simulate kernel UDP-buffer overflow.
|
||
|
||
Each variant has a deterministic semantics under the engine's virtual
|
||
clock. The fault catalog is the same value in scenarios and in
|
||
fuzzer-generated sequences; there is no "scenarios can do this,
|
||
fuzzer can do that" asymmetry.
|
||
|
||
Acceptance: the 2026-05-25 incident is expressible as a single
|
||
scenario file whose `faults` list is six or fewer entries drawn from
|
||
the catalog above. Replaying that scenario produces a bundle whose
|
||
diagnostics match the live bundle's shape within stated tolerance.
|
||
|
||
---
|
||
|
||
## 3. Mid-run invariants
|
||
|
||
After this work, the engine evaluates a declared set of invariants
|
||
continuously during a run. When an invariant is broken, the engine
|
||
records the violation and halts the run at the violating step. The
|
||
bundle's `verdicts.json` names the broken invariant, the virtual
|
||
time, the host whose state triggered the break, and the engine event
|
||
that immediately preceded it.
|
||
|
||
Invariants are written declaratively and registered against the
|
||
engine at scenario load. The minimum set covers:
|
||
|
||
- Membership convergence within a stated time of partition heal.
|
||
- No node alternates between alive and dead more than N times in a
|
||
window (anti-flap).
|
||
- No microbatch lives without a stage assigned to it.
|
||
- No stage is assigned to two distinct microbatches simultaneously.
|
||
- Monotonic counters in snapshots are monotonic across consecutive
|
||
snapshots.
|
||
- Every `SubprocessSpawned` event is eventually followed by either
|
||
`SubprocessExited` or `worker_ready`.
|
||
- No relay session reports `connection-closed` more than N times
|
||
against the same peer in a window.
|
||
|
||
The set is extensible. Adding an invariant is the same shape of work
|
||
as adding a post-run assertion today — there is no parallel API to
|
||
learn.
|
||
|
||
Per-invariant overhead is bounded: an invariant that requires reading
|
||
the full event stream every tick is not a valid invariant. The
|
||
contract is that the invariant set, in total, costs no more than a
|
||
small constant factor over a run with no invariants.
|
||
|
||
Acceptance: a scenario that injects the 2026-05-25 fault sequence
|
||
halts within the simulated second that contains the relay-close
|
||
event, reports the relay-close as the triggering engine event, and
|
||
the anti-flap or relay-session invariant as the broken one. A
|
||
developer running the scenario sees the failure in under a second of
|
||
wall time.
|
||
|
||
---
|
||
|
||
## 4. Seed-driven exploration
|
||
|
||
After this work, a single binary takes a scenario and a seed range,
|
||
runs each seed against the scenario, and reports the first seed whose
|
||
run violated an invariant. The report is the seed, the scenario, and
|
||
the broken invariant — sufficient input for the developer to
|
||
reproduce the run byte-identically with one further command.
|
||
|
||
The seed parameterizes:
|
||
|
||
- Initial RNG state for every host.
|
||
- The order in which the network resolves ties when two events are
|
||
scheduled for the same virtual nanosecond.
|
||
- The specific timing of each fault within its declared window (a
|
||
fault declared as "between t=1s and t=10s" picks one instant from
|
||
that window per seed).
|
||
- The distribution sample for any latency / size / count drawn from
|
||
a declared distribution.
|
||
|
||
A scenario without faults but with declared distributions still
|
||
benefits from seed exploration: the fuzzer probes the joint
|
||
distribution, not just the explicit fault list.
|
||
|
||
Parallelism is at the seed level. Running N seeds is N times the
|
||
wall time of one seed divided by the developer's core count, with no
|
||
shared state between runs.
|
||
|
||
Acceptance: a scenario file plus `--seeds 0..1000` produces, within
|
||
ten seconds of wall time on a developer machine, either "no
|
||
violations" or a printed seed that replays to the same violation
|
||
deterministically. The replay command and its output are the same
|
||
shape as a hand-written scenario run.
|
||
|
||
---
|
||
|
||
## 5. Heuristic extrapolation from known failures
|
||
|
||
After this work, every postmortem produces a *family* of scenarios in
|
||
the simulator's library, not a single scenario. The family is
|
||
generated by mutating the postmortem's parameters along axes the
|
||
implementer declares as "plausibly variable in the wild."
|
||
|
||
For the 2026-05-25 incident, the family includes at minimum:
|
||
|
||
- The original timing (relay session closes at +5s, never reopens).
|
||
- Sessions that close at +1s, +30s, +60s, +5min.
|
||
- Sessions that close with reasons other than `connection-closed`.
|
||
- Sessions closed from the relay side vs. from either endpoint.
|
||
- Sessions that flap (close + reopen + close, with varying
|
||
inter-flap durations).
|
||
- Sessions that close on only one direction of the tunnel
|
||
(split-brain at the relay).
|
||
- Sessions that close during convergence, during steady-state
|
||
inference, during shutdown, during a partition heal.
|
||
|
||
The mutation axes are part of the scenario family's source. The
|
||
fuzzer ranges over them; a developer reading the library can tell
|
||
what is being varied and why. New mutation axes are added when a new
|
||
postmortem shows the existing axes were too narrow.
|
||
|
||
Coverage is *the family*, not the single seed. A new SWIM tuning
|
||
change that fixes the original 2026-05-25 case but regresses any
|
||
sibling case in the family is caught before deploy.
|
||
|
||
Acceptance: the 2026-05-25 family contains at least the variants
|
||
listed above, each parameterized rather than copy-pasted. Running
|
||
the family against the current SWIM source either passes all
|
||
variants (the deploy is unblocked) or names which variant fails (the
|
||
deploy is blocked on that variant).
|
||
|
||
---
|
||
|
||
## 6. Boundary-condition probing
|
||
|
||
After this work, the fuzzer explicitly samples values near boundaries
|
||
where distributed systems are historically fragile, in addition to
|
||
sampling the interior of declared distributions.
|
||
|
||
The boundaries are:
|
||
|
||
- **Size**: messages at exactly the max-payload limit, exactly one
|
||
byte over, exactly one byte under. Piggybacked gossip just below
|
||
the size where the relay starts buffering.
|
||
- **Timing**: faults at exactly the suspicion-timeout, exactly one
|
||
tick before, exactly one tick after. Probes arriving exactly at
|
||
the deadline. Snapshots taken at the exact moment of a state
|
||
transition.
|
||
- **Counts**: peer counts at the minimum supported (N=2), one above
|
||
(N=3, where multi-region failure modes emerge), one above the
|
||
default (N=4). Fault counts that exhaust a recovery budget by one.
|
||
- **State transitions**: faults injected during a state transition
|
||
rather than in a stable state — drop the first ack after a node
|
||
enters Suspect, kill a subprocess between `spawn` and the actor's
|
||
first `recv`, partition during a relay's session-renegotiation
|
||
handshake.
|
||
|
||
These are not separate scenarios. They are sampling biases applied
|
||
to the seed search: the fuzzer spends a declared fraction of its
|
||
seeds at boundary values rather than at distribution interiors.
|
||
|
||
Acceptance: a scenario whose `faults` list includes a partition
|
||
declared as "between t=1s and t=10s" produces, across a fuzz run,
|
||
seeds that placed the partition exactly at SWIM's protocol-period
|
||
boundary and seeds that placed it one tick before and after. The
|
||
fuzzer's verdict is sensitive to this — a SWIM change that's correct
|
||
in the interior but wrong at the boundary fails the run.
|
||
|
||
---
|
||
|
||
## 7. Compound and asymmetric faults
|
||
|
||
After this work, scenarios and the fuzzer can express faults that
|
||
are simultaneously active, faults that overlap in defined ways, and
|
||
faults that are directionally asymmetric.
|
||
|
||
The required shapes:
|
||
|
||
- **Stacking**: two faults active during the same window. A partition
|
||
active during a relay-session flap. A clock skew active during a
|
||
subprocess respawn.
|
||
- **Asymmetry**: a partition that drops A→B traffic but allows B→A.
|
||
A relay-eviction that affects one peer's outbound but not its
|
||
inbound. Latency that is one-way slow.
|
||
- **Ordering**: fault X starts exactly when fault Y ends, or with a
|
||
declared overlap, or with a declared gap.
|
||
- **Multi-victim**: one fault scoped to one peer pair, another scoped
|
||
to a different peer pair, neither aware of the other.
|
||
|
||
Single faults are an under-sampled corner of the state space, not
|
||
the typical one. The implementations of (5) and (6) compose into (7)
|
||
by default — a postmortem family that mutates one axis at a time is
|
||
incomplete; the fuzzer samples joint mutations as well.
|
||
|
||
Acceptance: a scenario expressing "partition A↛B from t=2s, relay
|
||
session A↮R closes at t=3s, clock skew on B starts at t=4s" loads,
|
||
runs, and is replayable from `(scenario, seed)`. A SWIM regression
|
||
that is correct under each fault alone but wrong under the stack is
|
||
caught by the fuzzer.
|
||
|
||
---
|
||
|
||
## 8. Heavy-tailed distributions
|
||
|
||
After this work, every distribution the sim samples from has a
|
||
declared shape, and the shape defaults are heavy-tailed rather than
|
||
Gaussian.
|
||
|
||
Real network latency, real GC pause, real disk write, real subprocess
|
||
startup, and real cross-region RTT are heavy-tailed. A Gaussian
|
||
model with mean and stddev calibrated against a live bundle's median
|
||
will undersample the p99 by orders of magnitude, and most production
|
||
bugs live in the p99.
|
||
|
||
The sim's distributions are parameterized as
|
||
`(median, p99, max)` or `(median, shape, scale)` for log-normal /
|
||
Pareto, with the default-fitted parameters drawn from the calibration
|
||
bundles. A scenario can override per-link; the fuzzer samples each
|
||
seed from the declared distribution.
|
||
|
||
The fuzzer also exercises a "tail-amplified" mode that increases the
|
||
probability of drawing from the upper tail. This is the cheap
|
||
substitute for "run the sim for sim-years and hope a rare event
|
||
fires" — we move the rare events to the head of the distribution and
|
||
visit them in seconds.
|
||
|
||
Acceptance: a calibration scenario configured against `vastai-N3-2`
|
||
produces latency distributions whose p50, p95, and p99 fall within
|
||
stated tolerances of the live bundle's. The tail-amplified mode of
|
||
the same scenario produces a p99-heavy bundle in proportionally less
|
||
sim time.
|
||
|
||
---
|
||
|
||
## 9. Mid-recovery faults
|
||
|
||
After this work, the fuzzer routinely injects faults during recovery
|
||
phases, not only during steady state.
|
||
|
||
The recovery phases the sim recognises:
|
||
|
||
- During partition heal — the moment the network model resumes
|
||
delivery on a previously-cut link.
|
||
- During SWIM's transition out of Suspect.
|
||
- During an iroh relay-session renegotiation after a close.
|
||
- During a subprocess respawn between exit and the new process's
|
||
first protocol output.
|
||
- During the orchestrator's transition from "waiting for SWIM
|
||
convergence" to "ready to accept inference."
|
||
|
||
A fault injected during recovery is a different bug class from a
|
||
fault injected during steady state. The fuzzer should not have to
|
||
discover the recovery windows itself; they are observable in the
|
||
event stream (or in declared scenario phases) and the fuzzer uses
|
||
them as sampling targets.
|
||
|
||
Acceptance: a scenario that partitions, heals, and then partitions
|
||
again exactly during the heal-induced SWIM gossip burst, reproduces
|
||
deterministically and exercises a code path that the steady-state
|
||
version of the same partition does not.
|
||
|
||
---
|
||
|
||
## 10. Failure library and postmortem-driven growth
|
||
|
||
After this work, the simulator's scenario library grows by one
|
||
family per postmortem. The growth is part of the postmortem-closure
|
||
checklist: a deploy failure is not considered "closed" until the
|
||
sim's library contains a scenario family that reproduces it and the
|
||
fix passes the family.
|
||
|
||
The library is a directory; each family is a subdirectory containing
|
||
the original-incident scenario, the mutation-axes declaration, and a
|
||
short prose comment naming the failure and pointing at the
|
||
postmortem. The directory layout is part of the contract.
|
||
|
||
A postmortem that closes without contributing a family is allowed
|
||
only when the implementer states, in the postmortem, why the failure
|
||
mode is structurally unrepresentable in the sim — and that is a
|
||
separate behavior contract:
|
||
|
||
- **Sim-blind-spot inventory.** Each such postmortem appends an
|
||
entry to a `SIM_BLIND_SPOTS.md` adjacent to the library. The entry
|
||
names the failure mode and the structural reason. Closing a
|
||
blind-spot entry is a separate work item, prioritized by how often
|
||
that mode has been hit since.
|
||
|
||
The library and the blind-spot list together are the answer to "have
|
||
we tested for this." There is no third place.
|
||
|
||
Acceptance: the library contains a family for each of the eight
|
||
prior live failures. `SIM_BLIND_SPOTS.md` contains an entry for each
|
||
mode not yet representable.
|
||
|
||
---
|
||
|
||
## 11. Adversarial scheduling
|
||
|
||
After this work, when the engine has a choice of which of several
|
||
ready events to dispatch first (two messages scheduled for the same
|
||
virtual nanosecond, two timers firing simultaneously), it does not
|
||
choose uniformly at random. Under the seed-driven exploration of
|
||
section 4, a fraction of seeds use an *adversarial* tie-break: prefer
|
||
the dispatch order that exercises an under-visited code path or
|
||
crosses a state-machine boundary.
|
||
|
||
The adversarial scheduler is not a model checker. It does not
|
||
enumerate orderings. It biases tie-breaks by a heuristic — for
|
||
example, prefer delivering the message whose target host has not
|
||
received any message in the longest virtual time, or prefer firing
|
||
the timer that fires least often across the seed batch.
|
||
|
||
Cheap to implement, cheap to run, and historically effective at
|
||
finding race conditions in actor systems. The fuzzer's "adversarial"
|
||
mode is the lever that lifts seed-driven exploration from random to
|
||
targeted.
|
||
|
||
Acceptance: a scenario that has a known race condition (e.g. SWIM
|
||
ack arrives the same nanosecond as the suspicion timer fires)
|
||
produces a fuzzer verdict that includes that race even when the race
|
||
is reachable from only a small fraction of tie-break orderings.
|
||
|
||
---
|
||
|
||
## 12. Sub-second reproduction
|
||
|
||
After this work, the developer's loop is:
|
||
|
||
1. Run the fuzzer against the current source. Failure prints the
|
||
seed.
|
||
2. Run the replay command with the printed seed. Bundle written
|
||
under one second.
|
||
3. Inspect the bundle. The broken invariant is named; the violating
|
||
event and host are identified.
|
||
4. Edit the source. Re-run step 1.
|
||
|
||
Steps 1–3 are sub-second per iteration. The total loop time is
|
||
dominated by the developer's reading and editing, not by the sim.
|
||
This is the property that makes (5)+(6)+(11) worth doing — each
|
||
mutation costs nothing.
|
||
|
||
When the loop time grows above one second per iteration for a
|
||
3-node scenario, that is a regression in the simulator and is
|
||
addressed before further hardening work.
|
||
|
||
Acceptance: a continuous-integration job runs the full sim library
|
||
against the current source on every PR in under three minutes of
|
||
wall time on the project's CI tier. The same job, run locally,
|
||
completes in under thirty seconds on the developer's machine.
|
||
|
||
---
|
||
|
||
## Implementation order
|
||
|
||
Grouped by independence. Within a group, work is parallel-safe;
|
||
across groups, later groups depend on earlier groups' contracts being
|
||
agreed but not finished.
|
||
|
||
**Group A — production-code coverage**
|
||
- 1 (production code paths in the sim) — the load-bearing piece.
|
||
Until this lands, every other section's adversariality is testing
|
||
a model rather than the deploy artifact.
|
||
|
||
**Group B — fuzz and feedback**
|
||
- 2 (fault catalog) — depends on A naming the hosts that can be
|
||
faulted.
|
||
- 3 (mid-run invariants) — independent of B's other pieces.
|
||
- 4 (seed-driven exploration) — depends on 2 and 3.
|
||
|
||
**Group C — adversarial sampling**
|
||
- 5 (heuristic extrapolation) — depends on 4.
|
||
- 6 (boundary-condition probing) — depends on 4.
|
||
- 7 (compound and asymmetric faults) — depends on 2 and 4.
|
||
- 8 (heavy-tailed distributions) — depends on 4 only.
|
||
- 9 (mid-recovery faults) — depends on 4.
|
||
|
||
**Group D — library and process**
|
||
- 10 (failure library and postmortem-driven growth) — process
|
||
contract, can be drafted in parallel with any of the above.
|
||
|
||
**Group E — scheduling and loop time**
|
||
- 11 (adversarial scheduling) — depends on 4 and is cheap; lands
|
||
late because the gain is marginal until the rest of B and C are
|
||
in place.
|
||
- 12 (sub-second reproduction) — continuous obligation; a
|
||
regression in this section blocks merges of the others.
|
||
|
||
The eight prior live failures would have been caught with A + B + C
|
||
alone. D + E are how the next eight are caught.
|
||
|
||
---
|
||
|
||
## What this spec does not promise
|
||
|
||
- It does not promise that the sim catches every bug. It promises
|
||
that the sim catches the bug classes prior deploys have produced
|
||
and the bug classes structurally adjacent to them.
|
||
- It does not promise the sim replaces a staging deploy. It promises
|
||
that a staging deploy that follows a clean sim run is not a
|
||
diagnostic exercise — it's a confirmation.
|
||
- It does not promise that fuzz runs are exhaustive. It promises
|
||
that fuzz runs are dense around the parts of the state space we
|
||
have evidence are dangerous.
|
||
- It does not promise sim-prod fidelity at the byte level for every
|
||
field. It promises bundle-shape parity and behavioral parity for
|
||
the actors named in section 1.
|
||
|
||
A simulator that satisfies this spec is the gate between the
|
||
developer and the next two-dollar GPU bill. It does not eliminate
|
||
that bill; it earns it.
|