> ## Documentation Index
> Fetch the complete documentation index at: https://ctrlrun.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The soak, and what it does not establish

> One published run against a real Postgres: how long it lasted, how many actions it attempted, and how many ambiguous outcomes it could not attribute.

A soak asks one question, and it is not a performance question: **does an
`AMBIGUOUS` outcome ever appear that the harness did not cause?** Everything in the
run exists to make an unattributed ambiguity visible rather than rare. Here is the
one published run.

|                                   |                                                |
| --------------------------------- | ---------------------------------------------- |
| Ran for                           | 20m 0s (2026-09-05 19:05Z → 2026-09-05 19:25Z) |
| Backend                           | postgres (schema soak\_968eae6651)             |
| Actions attempted                 | 889,735                                        |
| Ended `AMBIGUOUS`                 | 133,393                                        |
| Explained by a recorded injection | 133,393                                        |
| **Unattributed**                  | **0**                                          |
| Positive control fired            | yes                                            |

The table is [`research/soak/results/2026-09-05-postgres-20m.json`](https://github.com/CTRLRun/ctrlrun/blob/main/research/soak/results/2026-09-05-postgres-20m.json), rendered.

## What that is evidence of

Across 889,735 attempts, every `AMBIGUOUS` the store held at the end had
a ledger entry written **before** the failure that produced it. Nothing became
ambiguous that the harness did not make ambiguous. The positive control fired, so
the run was capable of saying otherwise: a short control phase injects an ambiguity
it deliberately does not record, and the classifier has to report it. A run whose
control did not fire is not a result, and the table says which happened.

**Unexplained is defined before the run starts**, or the question is unfalsifiable.
An `AMBIGUOUS` whose attempt has a recorded injection is explained; one with no
corresponding injection is unattributed. It is keyed on the attempt and never on the
effect key alone, because one key may be attempted more than once and an injection
against the first attempt says nothing about the second.

**`ROADMAP.md`'s exit criterion is met by this run**: nothing unattributed, and
a positive control that fired, so the run was capable of reporting otherwise.
Those are the two halves and `SPEC-v0.6.md` §8.1 says they are the whole of it.
The criterion asks for no duration — §8.1 records when that stopped being true
and argues it — so what this run's length does **not** establish is the section
below rather than a footnote to a gate.

`exit_criterion_met` in the results file is those same two halves, written by the
harness. This page **recomputes** them from the counts rather than reading the
field, and a test asserts the two agree: one derivation of a criterion is a claim,
two that match is a check.

## What it is not evidence of

**A long run.** This one lasted 20m 0s. Everything a soak finds by
*accumulating* is outside what that can see: a connection pool that degrades over
hours, table growth against the one-row chain head, a lease that only lapses under
load held longer than this, an operator restart in the middle. The duration is on
the table above and in every place this run is quoted, because a criterion that
stopped asking for one is not the same as a run that no longer has one.

## What this does not do

* **It is not a load test.** The throughput is a by-product, nothing here is tuned
  for it, and no number on this page is a performance claim.
* **It is not a throughput figure you can plan against.** A policy denial writes a
  receipt like any other outcome, so nearly every attempt above went through the
  one-row chain head — but on one host, with four threads, against a database on
  the same machine. [Postgres](/docs/postgres) describes that ceiling; this run does not
  size it for your hardware.
* **It does not exercise a partition or a second host.** That is the cross-host
  suite, and [how reservation works](/docs/production/how-reservation-works) says which
  of those were actually run.
* **It says nothing about the receipt chain's integrity.**
  [Receipt integrity](/docs/production/receipt-integrity) is that.

## Run it yourself

```bash theme={null}
python research/soak/run.py --minutes 20 --postgres "$CTRLRUN_STORE_URL" --out soak.json
```

Worker threads share one schema, created for the run and dropped after it, and each
attempt is drawn from a fixed injection mix — mostly clean, the rest a timeout, an
unknown exception, a stated non-execution, or a lease short enough to lapse
mid-execution. The mix and the thread count live in the harness rather than here,
because a page that restated them would be a second copy nothing compares against:
[`research/soak/README.md`](https://github.com/CTRLRun/ctrlrun/blob/main/research/soak/README.md) has both.

**Verified by** the soak harness's own suite: an injected ambiguity is explained,
one with no injection is a finding, an injection against another attempt explains
nothing, and the control cause never explains anything — four tests, and the fourth
is what stops the control from absorbing a real finding. `T160` covers what an
expired lease does and does not free.

## Next

* [Run it in production](/docs/production/index): the section this belongs to.
* [Recovery after a crash](/docs/production/recovery): what survives a restart, and what does not.
* [Get started](/docs/get-started/quickstart) · [Why](/docs/why).


## Related topics

- [Running on Postgres](/docs/postgres.md)
- [Run it in production](/docs/production/index.md)
- [ctrlrun verify](/docs/verify.md)
- [Authority and delegation](/docs/concepts/authority-and-delegation.md)
- [Receipt integrity in practice](/docs/production/receipt-integrity.md)
