CLAUDE.md@packages/conformance · git:20261004.faec3ad · 2026-10-04 · sha256 3f869d178908fc04
CLAUDE.md@packages/conformance git:20261004.faec3adA
Immutable. This exact content never changes and is served at /api/v1/blob/3f869d178908fc04.
# conformance
## Purpose
The shared test infrastructure and the one package allowed to depend on
every layer: the storage conformance suite that *defines* backend
correctness, the wiring and jailed e2e suites that prove the production
effect seam (`client/wiring`, which once lived here before its promotion),
and the deterministic simulation runner that turns a seed into a verdict.
WP-T. Modules live under `src` (not `test`) so the suites form an importable
library. No other package depends on `conformance`, so today the only
importer is this package's own test tree: `storage_suite_test` runs the
storage suite over the backends.
## Key Types
- `conformance/storage_suite.{Backend, run}` — one suite parameterized over
a backend constructor. A backend passes WP-B's exit criteria only when
`run` is green.
- `conformance/simulation/runner.{run, check, examine, soak, Verdict,
Report, Corroboration}` — seed in, verdict out. A `Failed` verdict
carries a `Corroboration`: the seed is replanned and re-run up to
three times, and `Reproducible` means every run failed while
`Unstable` means the same script under the same schedule reached
different verdicts. Both, plus the run's own `[timing]` evidence, are
folded into `Failure.detail` so every existing printer shows them.
The reproduction text includes `make replay-simulation SIM_SEED=<seed>`,
which selects only the single-seed soak test rather than rerunning the
entire conformance package.
- `conformance/simulation/plane.{Plane, start, begin, finish, verify, stop}`
and `simulation/fake_jail.start_helper` — the effect plane. Each simulated
tool call starts a real execution through a broker over `broker/executor`
over a pool of fake helpers *before* the fault schedule is consulted, so a
crash, a strand kill, a slow park or an intervention lands with it in flight;
an effect the fault does not kill cancels it, hears its one settlement and
returns the scripted result, which never depends on the execution. The plane
belongs to `runner.execute`, not the tree, so a tree kill leaves it to be
inspected, and `verify` (called after the kill, with two seconds of real time
for effects to unwind) gives `effects/no-orphan` (no executor row, nothing
borrowed, every relay dead) and `effects/one-settlement` (a caller heard one
settlement and nothing after, and the executor's started equals its
completed + failed + lost). `Report.executions` and `Report.effect_violations`
carry them, `sound` raises them as named failures, and the coverage
assertion requires `effect-plane-execution` and `fault-during-execution`.
`simulation_plane_test` shows the checks failing on an execution nobody ends.
- `conformance/simulation/script.{Script, Op, Settle, Intervention}` — the
semantic half: what the session is *asked* to do. `Script.subagent` is
the multi-strand coda: an optional brief that spawns a subagent strand
and sends its findings back to main. `Script.parallel` *chooses* the
batch mode — `Parallel` when set (a parallel script always carries a
two-call batch, so the overlapping frontier is real), `Sequential`
when not. It names both rather than inheriting `api.default_options`,
which now ships `Parallel`; inheriting would make every seed a
parallel seed and lose half the oracle's coverage.
`Script.escalate` drives the durable escalation machinery — the first
clearance raises an escalation scoped to exactly that call, approves
it, and restarts the strand driver so the same durable coordinates
re-clear with the grant, all under the fault schedule.
- `conformance/simulation/daemon/harness.{Harness, Row, Snapshot}` — the
real daemon root, registry and catalogue over a temporary state root and
the simulation's logical clock, with no listener. It supplies the default
connection limits but does not exercise socket admission. The root starts a
listener only when a caller asks it to, creation takes its id generator
from the caller, and assembly is a record of caller-supplied callbacks,
so no seam had to be added to `client/daemon/root` to construct one from
a test. What runs for real is the catalogue writer and the conversation's
writer lease; the runtime and the domain services are not opened, so a
domain row's memory and search databases never exist under this harness.
Its operations are keyed by creation request key and canonical
session id; a `Row` has the run's temporary state root substituted out of
every path so two runs under two directories are comparable. `Boot` is
what one incarnation is started from, `Arrest` is where a simulated
builder parks so the run can kill the daemon at a named creation step,
`kill` is the untrappable kill of the root, the registry and the parked
builder, and `resume` is the start a faulted run restarts through.
- `conformance/simulation/daemon/daemon_fault.{Fault, Schedule, Step}` —
the daemon-level taxonomy, mirroring `simulation/fault`'s shape. One
fault, `KillDaemonAt(key, workspace, step)`, and four steps named by what
is already committed when the daemon dies: `AfterReservation`,
`AfterDomainBind`, `AfterCustodyPublish`, `AfterConfirm`. A kill is the
whole daemon and a restart over the same state root, never a registry
restarted in place, because the root answers a dead registry by blocking
recovery.
- `conformance/simulation/daemon/daemon_script.{Script, Creation}` — the
semantic half: one or two workspaces, one to three creation keys, and the
keys retried afterwards, with at least one reuse in every script. Drawn
from its own split stream so adding a draw here does not shift the
schedules.
- `conformance/simulation/daemon/daemon_runner.{run, plan, observe, describe,
Verdict}` — the seed's script run fault-free and then run again under the
seed's schedule, with the two compared. It carries its own `Verdict`
rather than widening the session runner's, whose `Report` is shaped for
one conversation, and reuses `runner.Failure` so the named checks print
the same way. Its checks are `creation/one-identity-per-key`,
`creation/no-orphan-file`, `publication/before-execute` and
`replay/equal-catalogue-rows`.
- `conformance/simulation/daemon/lifecycle_faults.{Fault, Pending,
Coordinate}` — the daemon-level lifecycle fault taxonomy:
`KillDaemonWithPending` names an interrupted `open` or `stop` by the
session it addresses, and `RevokeAt` names an owner revocation by where it
lands in a command's passage through the authority boundary. Both
coordinates are durable, for the reason the keying rule below gives.
- `conformance/simulation/daemon/lifecycle_runner.{run, restart_converges,
revocation_converges, Verdict}` — the two faulted daemon scripts. Each
runs fault-free and then under the schedule the seed drew, and the two
runs must leave the same catalogue rows at the same revision. Its named
checks are `lifecycle/reopen-policy`, `lifecycle/no-resend` and
`revocation/silence-after-close`. `lifecycle/no-resend` has two halves and
they are not held to the same draws: the stale-epoch half runs under both,
and the half that reads the session's status runs only under `PendingOpen`,
because a re-driven stop and a dropped stop both leave the session `Saved`
with no slot and the status could not tell them apart. `PendingStop` is
covered by `lifecycle/reopen-policy` and by the convergence comparison
instead.
- `conformance/simulation/daemon/domain_runner.{run, observe, plan, Script,
Schedule}` compares an open after normal domain retirement with an open
acknowledged while the original cleanup is held. A seed chooses the saved
session and duplicate count. `harness.HoldDomainRetirement` installs cleanup
through the real custody owner; the existing simulation control actor owns
the one-shot hold and records any build before release. Named checks cover
admission, stable operation identity, parked capacity, replacement ordering,
complete retirement, and equal catalogue rows and revision. This controls
callback release order, not BEAM interleaving or kernel enforcement.
- `conformance/simulation/daemon/daemon_soak.{Runner, Outcome, soak,
runners, describe}` — the daemon script's long run, bounded by a wall-clock
budget rather than by a seed count, because a daemon seed's cost depends on
the machine's file system and on whether the schedule drew a kill. It
corroborates a failing seed itself, over the session runner's three re-runs
and its `describe_corroboration` wording, since `daemon_runner` does not.
`runners` holds the creation-key, lifecycle and domain-retirement scenarios,
and every seed the soak draws runs each entry.
- `conformance/simulation/fault.{Fault, Schedule}` — the taxonomy of things
a session must survive without anyone noticing.
- `conformance/simulation/random.Rng` — a splittable SplitMix64; the only
source of choice.
- `conformance/simulation/vclock.Clockwork` — the one logical clock and its
timer wheel.
- `conformance/simulation/control.Control` — the counters, one-shot claims,
and runtime handle that outlive the session tree. Also
`control.Attempted`, the three-way outcome of running an action on a
disposable process: `Answered`, `Raised` (the carrier died — surfaced
immediately as a one-task `weft` run's `Crashed` outcome, so it costs
no wall-clock time), and `Expired` (a real millisecond budget ran out,
via `weft.deadline`). `Raised` and `Expired` are equally uninformative
about whether the action happened, and callers must treat them alike.
It is also the rendezvous a live-triggered intervention uses to hand
its decision to the runner: `await_intervention` registers a trigger
and blocks, and `take_pending_interventions` is what the drive loop
drains on every pass to find something to fire (#57).
- `conformance/simulation/{store, surface, invariant, wire}` — the
instrumented session, the scripted effect surface, the named per-run
checks, and the framing properties.
## Relationships
- **Depends on**: `simplifile` (the daemon simulation's `creation/no-orphan-file`
check enumerates the sessions directory from `src`, so it is a real
dependency rather than a dev one), plus every Gleam package it tests — `core`, `storage`,
`session`, `machine`, `runtime`, `provider`, `broker`, `tools`, and
`client` (whose promoted `client/wiring` the wiring and e2e suites
prove), and `prompt` (declared in `gleam.toml`, though no module
imports it today) — plus `gleam_erlang`, `gleam_otp`, and `weft` (the one-task
bounded run behind `control.attempt`). This is deliberate and unique.
- **Depended on by**: nothing. It is the leaf, and stays one: `client/demo`
copies the simulation's effect-surface shape rather than importing it,
because this package's surface is test support, not a library.
- **FFI**: `conformance/test/support/internal/ffi_time` and `ffi_shell` —
test-side only, for the jailed e2e harness.
- **Note**: the wiring adapter used to live in this package's `src`
because only the test leaf could depend on every layer; when the client
package became a real host it was promoted to `client/wiring`
(spec-gaps, M2 integration item 7 — resolved).
## Traffic
- **Actor messages**: `control.Message` (counters, claims, notes, the
runtime handle, the seam arm/quiet protocol, the harness-conduct
record behind `Report.waits`, and the atomic intervention claim) and
`vclock.Message` (now, schedule, advance, park). Both are calls. The
runner also subscribes to `runtime/writer.Event.Committed` indirectly
through the instrumented store's commit counter.
- **Commits**: `simulation/store.instrument` wraps a real memory backend's
`Storage` record so every commit passes through it — counted with a
counter that survives writer restarts, optionally refused as
`StaleExpectation` exactly as a concurrent admission would, and checked
against the boundary invariants at the transaction that caused them.
The same wrapper gives a memory session a **stealable lease**: a renewal
that fails once at a chosen commit and succeeds afterwards, which is what
a stolen-then-reacquired SQLite lease looks like from above.
- **Registers**: `simulation/invariant` reads `op.state`, `op.meta`, and
`strand.*` through `machine/codec` to check placement and terminal
register cleanup; `storage_suite` exercises every namespace generically.
- **Multi-strand traffic**: a share of seeds drive the subagent coda —
`api.create_strand` (fork-in-place at the main leaf), the child's brief
run to a terminal result, then `api.send_to_strand` delivering its
findings back to main as a durable steer-or-start admission. Coverage
now *requires* both the spawn and the cross-strand message, so crashes
during a child's effects exercise the boot-all-strands recovery path —
and, since the M3 escalation-boundary wave, also the parallel frontier
(`parallel-tools`), the escalation dance
(`escalation-raised`/`escalation-consumed`), and the partial crash
(`strand-restart-during-effect`). Terminal detection prefers the
operation-keyed `operation-result/{op}` record and reports a violation
when a terminal is reachable only through `strand.last_result`.
- **Wire**: `simulation/wire` generates byte streams straight into
`broker/framing`'s deframer — no helper involved; the jailed e2e drives
the real `loom-exec` through the broker.
## Invariants
- **A failing seed's report is printed before the panic.** `simulation_test`
prints its full report to stderr and then panics with the same text,
because eunit truncates a panic message and a gate log that carried only
the truncation showed the first check's name with no seed, no reproduction
line, and none of the `[timing]` and `[verdict]` evidence the runner paid
to collect (issue #335). Any new suite here that fails over a range of
seeds owes its log the same thing.
- **Faults are transparent by definition.** A schedule may crash the tree
at a commit boundary, kill it mid-effect, restart a single strand
driver mid-effect (`RestartStrand` — the partial crash the reaper must
survive), refuse a commit as stale, fault a read, steal the lease, drop
or delay a doorbell, starve an effect, or lose an effect process — and
none of it may change what the session ends up having done. Anything
that legitimately changes the outcome (a provider that refuses, a user
who aborts) is scripted into *both* runs so it cannot be mistaken for
damage.
- **A provider request is never parked on the logical clock** (#335). It
runs inside the runtime's provider deadline (`surface.provider_timeout_ms`,
60 ms) and two-second cancellation grace, and both are real time. A
`SlowEffect` park ends only after the runner has advanced the clock
through every earlier deadline, so its real length is the host's: about
165 ms idle and four to seven seconds under load for a 2000 ms park. The
loaded case outlived the grace, settled the attempt as
`CancellationUnconfirmed`, and folded a steer into the retry, one turn
short of the fault-free run (seed 14, `convergence/ledger`).
`surface.request` therefore applies only the survival faults; a
`SlowEffect` drawn on a provider index does nothing, while tool
executions, which have no runtime deadline, are still slowed. A live
intervention's rendezvous still runs inside the deadline, and a
settlement that misses it arrives inside the grace and is forwarded
unchanged.
- **Starved provider owners end with their consumers.** A starved provider
effect is a weft witnessed run (`weft.new_prepared([weft.managed(...)])
|> weft.cancel_when_exits(consumer) |> weft.start_witnessed`), and its
scope pid is `stream.owned`'s owner. The one-shot timeout owner — the
racer that holds the preselected transport-failure terminal — is
adopted as a leaf, so cancellation asks it to stop instead of killing
it, and naming the consumer here is what makes a killed effect cancel
the run through the same path an explicit release takes. A killed
effect therefore removes both the scope and its unused terminal
capability instead of leaving a synthetic process behind the
conformance run; its release still reaches the racer, but the send has
nowhere left to land, since `events` is owned by the consumer that just
died.
- **A composition-layer service is proved absent, not merely
well-behaved.** `conformance/triggered_rules_test` is the end-to-end
for issue #27: real wiring, real gateway, real adapter, real runtime,
a real supervisor over the real scanner, and only the HTTP transport
scripted. Its isolation row kills the scanner while a turn is in
flight — an absent scanner being the strongest available form of "an
expensive scan cannot delay a settlement" (design §10) — and asserts
the transcript is exactly what the script implies, request for
request, before showing the rule still fires off the durable marks
once the supervisor has replaced it. The one piece of stage machinery
is a transport that waits for the durable fired-mark from the second
turn on, which settles the scenario's only race — was the steer
durable before the checkpoint that drains it — without touching
anything under test.
- **A daemon crash is a root restart over the same state root.** Killing the
root takes the registry with it, because the lifetime scope is linked to the
root and the registry is owned by that scope. The lifecycle scripts kill the
registry by name as well, so that they have a monitor to wait on for each
process rather than a cascade whose completion nothing reports. The next
daemon takes the same state root and has to poll for it, since the launch
lock is released by the operating system and is not ordered against the
monitor that reported the death.
- **Without a listener, "pending" and "principal" are registry-level
terms.** A pending lifecycle request is one `manager.open` or
`manager.stop_session` answered with an accepted operation and had not
settled; a principal is an invited member with a credential digest and a
session grant, and the boundary it crosses is `manager.frame_authority`.
A command in flight is the pair of authority answers the transport asks
for, once at admission and once at delivery, so a revocation landing
between them is expressible here. What is not expressible is the socket:
that no frame is written to a closed connection is a gateway claim, and
`tui_shipped_multiplayer_test` is what proves it.
- **The two revocation coordinates are two schedules, and only one of them
fills the memo.** The registry remembers a resolved grant for the lifetime
of a session's slot and drops the whole memo when an administration commits.
`BetweenAdmissionAndDelivery` admits the principal before the owner revokes,
which both fills that memo and gets the command through admission;
`BeforeAdmission` revokes before the boundary has answered anything at all,
so nothing is remembered and nothing is queued. Removing the memo drop in
the `Administer` arm of `manager.gleam` therefore fails
`revocation/silence-after-close` under the `Between` seeds and passes under
the `Before` ones, which is the discrimination the coordinates exist for. A
run that made both coordinates admit first would be one schedule counted
twice.
- **Nothing is keyed by a counter.** A generation request is answered by
the *phase* of its projected context — how many assistant messages are in
it, plus a hundred once a summary is — and a tool execution by its
scripted call id. Errored, aborted, and deferred responses never enter a
projection, so a synthetic settlement written by recovery cannot shift
the phase, and a script means the same thing under every schedule.
- **Answered-call checks scope to *executable* responses.** Faithful abort
retention means an aborted entry can carry tool calls that never ran, so
demanding a result for every call in every retained response is wrong.
- **Split-summary progress is counted at the commit boundary**, not at
send time: a settlement lost with a halted strand must lead to another
request rather than to a summary the ledger never paid for.
- **The wall clock is a deadlock backstop, and says so when it fires.**
`control.attempt` is how anything reaches into a tree that may be
mid-restart. It runs the action inside a one-task `weft` run bounded
by `weft.deadline(within_ms)`; that budget is real milliseconds — it
cannot be logical, because the action blocks on a real OTP call and
the runner is the process waiting on it. What used to reach that
budget routinely was the carrier *dying*; that is now surfaced
immediately as weft's `Crashed` outcome (mapped to `Raised`) and costs
no clock, which leaves the budget bounding only a wedge nothing is
expected to reach. Every `Expired`, every `Raised`, and every scripted
intervention claimed but never seen to land is recorded through
`control.note_wait` into `Report.waits` and named in the failure the
run reports. Neither `Raised` nor `Expired` is evidence the action
failed: an admission whose reply was lost may already be durable, and
every caller asks the durable state rather than concluding (issue #44,
and the same ambiguity #6 records).
- **A retry waits for the writer, not for a pause** (#335). The runner's
admission ladders (`admit`, the subagent's creation and brief, and the
cross-strand send) count their attempts. `before_retrying` advances the
clock one step and then `await_writer` polls through `weft/poll` until
the root has registered a writer or died, so an attempt is spent only on
a writer that could answer it. The fixed one-millisecond pause this
replaced gave a ladder a real budget of a few milliseconds: on a loaded
host, a crash on the commit before the subagent's creation spent all six
attempts on an unregistered writer, and the run reported
`run/terminated` with a clean timing line (seed 1). The wait's
3000 ms bound is a backstop, recorded as `expired@writer-restart` if it
is reached.
- **A failing seed says whether it is repeatable.** `run`, `examine`, and
`check` replan the failing seed — same script, same schedule, since
`plan` draws only from the seed — and re-run it up to three times
before reporting. Shrinking is skipped for an unstable seed, because
"does this smaller schedule still fail?" is answered by coin toss
there. An unstable verdict is not an all-clear: a genuine race in the
code under test is unreproducible too, so what it licenses is
comparing *rates*, never dismissing the run.
- **A daemon script's identities come from the logical clock and the
seed, and from nothing else.** `manager.create` mints a session id from
the generator its caller supplies, so a daemon harness reading the
simulation's `Clockwork` mints the same ids on a replay. Everything else
a run leaves behind is derived: the database path from the id, the
creation time from the clock, the domain record from the workspace. The
daemon script's convergence check compares those rows and the catalogue
revision they were read at, because a run that wrote a row twice would
carry identical rows at a higher revision.
- **The daemon harness's waits are deadlock backstops, not simulated
time.** `manager.create` returns while a builder still owns the
operation, so the harness polls the registry (`weft/poll.until`) for the
published incarnation before reading a row back. Those milliseconds are
real, because the registry is a real process; what the logical clock
governs is what gets *minted*, which is what a replay has to agree on.
A daemon run therefore has more real processes than a session run, and
its failure rate over a seed range is the signal rather than any single
red.
- **Fault addressing is ordinal, not temporal.** Commit-indexed faults name
a global commit ordinal counted across writer restarts; effect-indexed
faults name a dispatch ordinal. Neither is a wall-clock instant, so a
schedule means the same thing on a loaded machine as on an idle one.
- **A simulated session never sleeps.** The clock moves only when the
runner moves it, and by exactly one step: to the earliest registered
deadline. Firing a deadline early is always safe — the driver re-reads
its durable state on every pass, so an early wake costs one wasted
planning pass. A deadline is removed when it fires, so a wake delivered
to a dead process is simply lost, exactly as a real timer's message
would be. The drive budget counts consecutive silent passes, not the
operation's lifetime: every writer commit proves durable progress and
replenishes it. Charging progress against the stall allowance made loaded
Linux runs fail at moving seeds even though each immediate replay passed.
- **One clock, one era.** The storage backend's timestamps, the strand
driver, the effect scripts, and id minting all read the same `Clockwork`.
Separate real clocks across runtime, tools, and broker drift, and that
drift once made the broker refuse every call as past deadline (spec-gaps,
M2 item 1).
- **Every check has a name.** "The simulation failed" is not a bug report.
Boundary checks run inside the commit path so a violation is caught at
the transaction that caused it; terminal checks run once the strand is
idle.
- **An intervention that does not land is a violation, not a shrug.**
`surface.apply` honors the `Result` of `api.steer_quietly` and
`api.follow_up`: a refusal is a turn the transcript has permanently lost
and it is recorded through `control.note` — `Report.violations`, which
`sound` fails the seed on. Two refusals are deliberately exempt and
marked instead of noted: an `AtTerminalCommit` steer, where the run the
item would attach to is already closed and refusal is the documented
outcome, and an admission whose reply the surface did not observe,
which is not evidence the commit failed. `api.RuntimeUnavailable` is not a
refusal: no request reached a writer. It goes through the existing durable
fact reconciliation and retries while the root can restore the writer.
The ordered batch stops at the first unavailable admission; its untouched
suffix cannot overtake it, including an abort from the same trigger.
The recording lives on an
error path and touches no schedule, so the seed corpus keeps its
meaning as a before/after oracle. The runner takes the intervention's
in-memory one-shot claim, while the instrumented store derives the same
identity from the queued message's opaque signature and folds a reserved
write-once fact into the admission transaction. A retry after a lost reply
can therefore ask whether the payload is already durable: a landed first
attempt closes the debt, while an absent fact permits a new carrier without
admitting the turn twice. When queue admissions and an abort share one
logical trigger, `surface.fire_due` preserves the queue admissions' script
order but sends the abort after them. The abort API is an asynchronous cast;
sending it first made seed 584's fault-free transcript depend on whether the
Linux or macOS scheduler processed the cast before the next synchronous
admission.
- **A live intervention's decision belongs to the runner, never to the
effect that reached its trigger** (#57, diagnosed under #44). A
`DuringTurn`/`DuringCall` trigger is reached from inside a real effect
process, and a `RestartStrand` fault reaping that process before
`control.attempt`'s own bookkeeping ran meant some lost admissions
carried no `raised@`/`expired@` tag at all — a silent dangling
`intervening@path` instead of one with a cause attached. So
`surface.intervene` no longer admits: it only asks whether the script
has anything due at this trigger and, if so, calls
`control.await_intervention` and blocks without an independent timeout.
This rendezvous carries a payload, not a loss-tolerant wake-up: resuming the
effect while its trigger remained queued would let the admission land after
the settlement it must precede. The runner's drive loop
(`service_interventions` in `runner.gleam`, called from `pump_strand`
on every pass) drains `control.take_pending_interventions` and fires
each one through `surface.fire_due`. The block is
what keeps a live-triggered steer landing before the settlement that
follows it: the effect does not resume until the runner reports the
admission durable, so the ordering `steer-during-effect` depends on is
still by construction, not by luck — only the process carrying the
admission moved, from the effect to the runner, which is never a
target of any fault in the taxonomy. If that runner-owned carrier loses its
writer call during restart, or receives the typed pre-send availability
result, `surface` reads the transaction's atomic fact
directly from the raw durable session: it settles an admission that already
landed, or retries one that did not. There is no post-commit counter for a
crash to overtake. The `intervening@path` / `intervened@path` bracket remains
as a tripwire for any future path that spends a claim without making the
payload durable.
- **`terminal/last-result-once` is fenced across commit visibility** (#58).
The memory actor can publish a terminal transaction before the
instrumented wrapper records its side counters, while the runner reads
that inner actor directly. `CommitStarted`/`CommitSucceeded` hand the
accounting fence atomically to the post-commit seam, keeping `seam_quiet`
false through successful bookkeeping; `CommitFailed` releases refused
commits. The deterministic `simulation_store_test` probes immediately
after the raw commit becomes visible and checks both success and failure.
The production terminal transaction and the exact once oracle are
unchanged. See `docs/architecture/simulation.md`, "What this does not
cover".
- **A fence or seam is held by a live process, never by a flag** (#335).
A `CrashDuringEffect` kills the writer from another process, so the kill
can land between `commit_started` and `commit_succeeded`, or inside a
seam the writer opened after the crash note was sent. The control actor
keys each fence and the open seam by the committing pid, and
`seam_quiet` counts only holders that are alive. A dead writer's hold is
released by its death, with no message racing the kill, so the runner
cannot wait out its idle budget on a visible terminal result. The
bookkeeping the dead writer had not done is lost with it, as it is for
any crash. `note_crash` records only that the crash fired.
- **The perf smoke asserts, it does not merely print.** `storage_suite_test`
holds the `scan_branch` p50 to `perf_p50_ceiling_us` (15 ms) rather than
to the 5 ms M0 target it reports against — shared CI hardware has
produced a *max* above 5 ms on a run whose p50 was 3.1 ms. What that
bound proves is that the scan still costs single-digit milliseconds on
the machine that ran it; what it does not prove is the M0 target, and
the `EXPLAIN QUERY PLAN` assertions next to it remain the precise guard
on which index the scan uses.
- **The deframer must be total** and is property-checked as such: chunking
is irrelevant, damage is reported rather than silently survived, and
truncation carries the partial bytes for a rest that never comes.
- **A daemon fault kills the root, and the run restarts over the same state
root.** The root answers a killed registry by blocking recovery rather than
restarting it in place, so a registry replaced under a live root is a state
production never reaches and a check written against it would be checking
the harness. `harness.Boot` therefore takes the state root from its caller
so the next start rebuilds from what was committed.
- **A kill names three processes, and waits on each.** The root's death
cascades through the lifetime scope to the registry, but nothing reports
when that cascade has finished, and it does not reach a builder parked in
the harness's assembly callback, which the scope holds no link to.
`harness.kill` therefore kills the root, the registry and the parked builder
`park` reported, and waits for a monitor on each; a kill of a process the
cascade already took down delivers DOWN immediately, so the extra kills are
free and the order carries no meaning. It is still not a drain: the launch
lock is released by the operating system after the root's port closes, which
is why a restart goes through `harness.resume` and its bounded retry rather
than a single `start`. The dead lease is retired by the session tree
asynchronously, unordered against the restart, which is why the restart
moves the lease clock instead of waiting.
- **Logical time advances by one lease lifetime per incarnation, and
nowhere else.** A killed daemon leaves an unexpired writer lease in the
conversation file it was building, held by an owner that no longer exists.
A restart reading the same instant would refuse its own reservation with
`LeaseHeld` for as long as the run lasts. `harness.Boot.incarnation`
moves the clock the writer lease is read against, and only that clock: the
identity generator keeps reading the unadvanced logical clock, so a
restart does not shift the timestamps a later creation mints and the two
runs of a seed stay comparable. The advance is kept even though no lease
holder survives the kill, because a lease is time-based: a restart inside
the TTL is refused however dead the owner is, and that case belongs to
`client`'s `daemon_shipped_identity_recovery_test`.
- **Domain services are never opened under the daemon harness.** A domain
row names a memory database and a search index, and neither file exists in
a simulated run, so `creation/no-orphan-file` counts conversation
databases and nothing else.
- **`let assert` is permitted in this package's `src`**, unlike everywhere
else (spec §0.2). A suite or runner whose fixture will not construct has
nothing to say; the module docs state the exemption where it is used.
Lint R4 (`panic`/`let assert` outside tests) now gates the whole tree at
**error** level, and it reaches this package's `src` the way it reaches
everyone's — so the exemption is named rather than implied:
`lint/policy.harness_packages` lists `conformance`, and `lint.check`
applies it from the path. Nothing else in the tree is on that list, and
adding to it is a decision to be argued in `packages/lint/CLAUDE.md`'s
Staging section, not a prefix that grows quietly. The ninety `let
assert`s here were a third of the whole lint census and carried no
signal, which is what kept the one rule the root `CLAUDE.md` states as
policy from being enforced anywhere.
The exemption covers the **construct and not the message**. Part IV rule
3 admits `panic`/`let assert` only "always with an `as \"message\"`"
naming the invariant. R4 checks presence rather than message; R7
(`assert-without-message`) checks the message and warns, including in
this package's `src`. Only `test/` disables R7. Bare assertions remain
lint debt, not an exemption from the guide.
## The language-server suites
One test module drives a real language server through the jail,
feature-detected and printing `SKIP <suite>: <what is missing>` where a
prerequisite is absent (CI's skip census decides which skips a lane may
have; `.github/declared-skips*`):
- `lsp_e2e_test` — issue #25's acceptance: whole sessions booted by
`serve.open_instance` from a `loom.toml` with an `[lsp]` table, a
scripted model renaming across a Gleam fixture (and a stale apply), and
`gopls` through the tool path. It looks executables up with
`jail.find_executable` and reads the Go toolchain's directories from the
environment `go` reads, so it needs no shell command.
The language profiles are not tested here. They live in their own
repositories (ADR-016, addendum), whose CI builds Loom and runs
`loomd ext check`. The mechanism they depend on is tested in `client`
(`client/extension/profile_test`, `client/lsp/profile_check_test`).
The suite demands `exec.BestEffort`: the ordinary runner's helper cannot
apply every layer platform enforcement demands, and the probe would
refuse the server before any question. The jailed Linux lane installs
`gopls` and runs the Go session with no waiver.
## Deep Docs
- [docs/architecture/simulation.md](../../docs/architecture/simulation.md) —
seed/script/schedule/verdict, keying, simulated time, the fault taxonomy,
reproducing a failure.
- [docs/architecture/durability.md](../../docs/architecture/durability.md) —
"The conformance suite is the definition of correct".
- [docs/spec-gaps.md](../../docs/spec-gaps.md) — "From WP-B/T" and "From
the M2 integration (`conformance/wiring`, since promoted)".
- [Root CLAUDE.md](../../CLAUDE.md) — repo ground rules and the doc graph.
`make conformance` runs the suites; `make e2e` the jailed acceptance;
`make soak` the long session simulation run, and `make soak-daemon-sim`
the daemon one.
## Code-mode LSP monorepo acceptance
`lsp_e2e_test.lsp_code_mode_monorepo_end_to_end_test_` boots the production
session with a prepared code-mode seed, a scripted provider, a real helper,
and a Gleam profile. The submitted program imports `cap/lsp` and queries a
package that imports a sibling path dependency. Its durable tool result must
contain a nonempty outline and the exact hover signature. It prints a skip
when the toolchain, helper, or seed is absent; such a skip is not an end-to-end
pass. The seed is prepared with `make codemode-seed`, as for the code-mode
acceptance lane.