# Relay

**A ratchet loop where the run outlives the session.**

Version 5.0-beta · An edition of the Ratchet Loop for Grok Build · Free to copy

*5.0 changes nothing about how a round is judged and everything about who keeps the run alive.
The Ratchet Loop 4.3 (`RATCHET-LOOP.md`, beside this file) is the method; Relay is that method
run under a **supervisor that lives outside the model** — a plain script that starts one fresh
Grok Build session per round, hands it the RESUME block, waits on it by PID with a deadline,
checks the record it left on disk, applies your caps from the harness's own spend report, and
starts the next one. Every rule in 4.3 still binds a Relay run. What Relay adds is the thing
§15 of that spec says no prompt wording can add: a session that is as long as the round rather
than shorter than the run.*

*It is a beta, and this document says so where it matters. Nothing here has been measured on a
paid run. §12 says what the first measurement is and what the first ten sessions will tell you.
The supervisor, the boundary check, the stop gate and the doctor are tested against synthetic
runs — a hundred and forty-six cases, in `relay/test/` — and against nothing else yet.*

---

## 1. What this is

Seven real multi-day runs of the Ratchet Loop produced a fleet of survival data, and the shape of
the deaths was the same each time: the session died, not the round. Measured sessions ran 8h41m,
8h00m and 8h42m; the resumes after them ran 2h00m and 24m32s, each shorter because a fresh context
spends its first stretch re-reading. §12.2's RESUME block made that re-read a page. It could not
make the session longer, because nothing a prompt says can.

**Relay inverts the relationship.** Under 4.3 the run is a long session that hopes to reach the
next boundary; under Relay the run is a sequence of short sessions, each started at a boundary,
each told to reach the next one and end. The context is fresh every round by construction, so
compaction is a thing that happens rarely rather than a thing the method spends a section
surviving. The RESUME block stops being a fallback for when a session dies and becomes the one
document every session starts from.

Three consequences follow, and each is a mechanism in this edition:

- **§12.3 — "ending a turn is ending the run" — becomes false, on purpose, and then true again
  in a narrower form.** A session is supposed to end. What it must not do is end mid-round, and
  whether it has is a question a script can answer from disk: was a ledger line appended since
  the session began, is `PENDING` gone, is any child still alive. §6 is that script, registered
  as a `Stop` hook.
- **§9's boundary checks move out of the model.** Every one of them — the snapshot, `PENDING`,
  the ledger that must only grow, the fields §13 says never move — is a comparison between
  files. §5 performs them after every session and stops the run on a fault. A script cannot be
  persuaded that an orphan is the champion.
- **The brake is a number the harness reported, not a number the run remembered.** Grok Build's
  headless JSON result carries `usage.total_tokens` and, when complete, `total_cost_usd`. §8
  reads them off every session, sums them on the supervisor's own ledger, and stops before the
  session that would cross a cap. The ledger's `cost` field, which 4.3 fills from a difference
  in `/goal status`, has a second source that does not depend on the model reading a screen.

**When to run this rather than 4.3 as it stands:** when the run is long enough that a session
will not reach the end of it. That has been every run this project has recorded.

---

## 2. Honest prior art

The idea of a supervisor is not new and the composition is not either. What was looked at, and
what was done with it:

| Part | Where it comes from |
|---|---|
| A fresh session per unit of work, resumed from a handover on disk | §12.2 and §15 of the Ratchet Loop, which asked for exactly this and said the prompt could not supply it |
| The `Stop` hook as a keep-working gate | Grok Build's documented hook system, and Claude Code's, whose vocabulary it shares |
| Headless sessions launched by a script, with spend read off their JSON | Grok Build's headless mode documentation; the `claude -p` pattern in ruvnet's Ruflo |
| Deterministic checks doing what a model was doing | Ruflo's tiering — do the things that need no judgement with no model — and its MetaHarness setup audit |
| The supervisor owns every PID it starts, terminates only by PID, takes a lock, never touches what it did not launch | This machine's shared-project rules; §12.4's "the machine is part of the run" |
| Land inside the budget: do not start a round the median one cannot finish | 4.3's `CAP_SPEND_LANDING`, moved from the run's estimate to the supervisor's arithmetic |

**What Ruflo has and this edition deliberately does not take,** because the Ratchet Loop's own
measurements point the other way: swarm topologies and consensus algorithms (§7.2's three-of-four
with both presentation orders is the consensus rule, and it is tuned to position bias); a
self-learning memory fed back into prompts (blindness depends on fresh-context judges, and every
wrong stop this project measured came out of vocabulary the run had learned); a hundred agent
types and automatic routing (five roles, each with a different context and job, §3). Ruflo is an
excellent answer to a question this method does not ask. What it confirmed is that the one
ingredient the Ratchet Loop lacked was the cheapest one on its list.

**And one thing Grok Build's own design cuts against, which Relay closes.** Its cross-session
memory, when enabled, injects prior-session context on a session's first turn and again after
compaction. For a critic or a judge that is supposed to be fresh, that is a blindness leak.
Every session the supervisor starts carries `GROK_MEMORY=0`. The kit's doctor reports the
setting; nothing in this edition relies on it being off in your interactive window.

---

## 3. The supervisor is not a role

§3 of the method has five roles and a sentence about why it does not have a sixth: "a role with
no job is a role a run will invent work for." The supervisor is not a role. It is not an agent.
It has no context window, reads no artifact, holds no opinion and cannot be argued with. It is
`relay.mjs`, about six hundred lines of Node with no dependencies, and everything it knows about
the run it reads off disk.

It does five things, in order, forever, and nothing else:

1. **Reads the record.** Every ledger under `gauntlet/` — the Duel's one or the Ratchet's
   per-piece ones — the `PENDING` files, the RESUME block in `workbench.md`. Writes what it saw
   to `gauntlet/relay/session.json`, so the stop gate (§6) can compare against it.
2. **Starts one session.** `grok --prompt-file <hand-off> --output-format json --always-approve
   --max-turns N --cwd <root>`, with `GROK_MEMORY=0` and `RELAY=1` in its environment. The
   hand-off is the kit's, with four lines of session facts in front of it: which session this
   is, when it started, when its deadline is, how many ledger lines existed when it began.
3. **Waits by PID, with a deadline.** §5.4's rule at the session level: three times the median
   round wall over the last three ledger lines, never less than the configured hard wall (six
   hours by default). A session past its deadline is dead, not late: its process tree is
   terminated by PID. So is one whose heartbeat — `workbench.md`, `progress.html`, the ledgers,
   its own stderr — has been silent for longer than the configured window with no line appended.
4. **Checks the boundary.** §5. A fault stops the run.
5. **Applies the caps and records the session.** §8. One line on `gauntlet/relay/relay.jsonl`,
   `STATUS.md` rewritten, `ownership.md` rewritten, and round again.

It never builds, never judges, never reads a sealed check, never writes under `gauntlet/` except
in `gauntlet/relay/`, and never edits a file the run wrote. It takes `.agent.lock` at the repo
root for the life of the run and releases it in a `finally`, on success, on failure and on
Ctrl+C. It terminates nothing it did not start, and nothing by name.

---

## 4. The session

**One round per session, and the round ends the session.** The hand-off tells the session which
round it is, what the RESUME block should name, and to end its turn at the boundary. The method's
own ON STARTUP rule does the rest: a ledger with lines means setup is done and round 0 is over,
so the session reads the RESUME block and the last ledger line and starts the round the block
names. The first session has no ledger and is SETUP plus round 0.

**The prompt gains one block and loses one line.** `PROMPT.md` in a Relay kit carries a `RELAY`
block after its harness section: one round per session, end the turn at the boundary, the Stop
hook refuses a mid-round end and says why, the supervisor is the brake. The `/goal` line at the
top of a Grok prompt is not there, because the supervisor is the goal loop: a headless session
is started with a prompt, not with a slash command, and the completion review `/goal` runs is
replaced by the boundary check in §5, which reads files rather than asking the run.

**What a session may still do.** Everything 4.3 lets it do inside a round. Spawn its eleven
children, wait on each by id with a deadline, write the ledger line, rewrite the RESUME block,
remove its worktrees. Compact, if a long round fills the context — the RESUME block is still
rewritten at the boundary and read at the next startup. Write `"state":"PAUSED"` at a boundary
it can reach when it cannot finish the round, and say why in `workbench.md`; the next session
resumes.

**What a session may not do.** Start a second round. Two rounds in one session is recorded as
an `overrun` on the supervisor's ledger and the run continues, because two honest rounds are not
a fault; but the second one ran on a context the method meant to refresh, and the count is there
so you can see how often the instruction was not followed.

**The two sentences of 4.3 this supersedes, named.** "Every rule in 4.3 still binds" is true of
everything inside a round and of the record; two sentences about the *session* cannot survive a
session that is one round long, and a run reading both documents should not have to work out
which wins. They are: §12.3's "after a compaction, read the RESUME block and start the next
round" — under Relay the next round is the next session's, and the boundary is where a session
ends; and the hand-off's "this objective is complete only when a cap I set has fired or I have
stopped you" — under Relay the caps live in `relay.json` and fire between sessions, so no session
ever sees one fire, and a session is finished at its boundary, not at the run's. The Relay kit's
hand-off and its RELAY block carry the replacement wording; where the unchanged method spec says
the old thing, the RELAY block wins, and this paragraph is where that is decided.

---

## 5. The boundary check

`check-round.mjs`, after every session, comparing the state the supervisor wrote down before it
against the state on disk after it. Two kinds of finding, and the split is §9's.

**Faults stop the run** and deliver the last champion whose snapshot still matches:

| Fault | What it is |
|---|---|
| `snapshot-mismatch` | a champion directory the ledger names no longer hashes to its own `SNAPSHOT.sha256`, by the §8 algorithm — sorted relative paths, itself omitted, one `sha256  path` line each, the sha256 of that listing |
| `champion-missing` | a round the ledger says promoted has no champion directory on disk. Round 0 included: its line is the first champion (§4.2 step 6). A per-piece ledger's round 0 may sit under the shared `gauntlet/champion/` path from before the pieces were cut; either location counts |
| `criteria-changed`, `criteria-missing` | `gauntlet/criteria.md` no longer hashes to the first line's `criteria_sha` — or is not on disk at all, which disagrees harder |
| `pieces-changed` | `decomposition.pieces` differs between any two lines (§13) |
| `ledger-shrank`, `ledger-rewritten`, `round-regressed` | a ledger has fewer lines than before the session, or a line that existed before has changed, or the highest round id went down. Append-only means the prefix is byte-identical afterwards. A last line that does not parse and has no newline — a session killed mid-write — is *open*: not a line yet, in neither count nor hash, so the next append landing on it is the append it is |

**What a fault delivers.** The newest ledgered champion whose directory is on disk and still
hashes to its own snapshot — which is not always the one the ledger names last. When the fault
*is* the current champion's snapshot, naming it would hand over the one artifact the method exists
to reject; the supervisor's stop message names the one that verifies, or says plainly that none
does.

**Warnings are recorded and the run continues**, because each is a state the method's own
ON STARTUP rule already handles in the next session:

| Warning | What it is, and who handles it |
|---|---|
| `no-progress` | no line appended. Three in a row end the run (§8) — a session shape that keeps producing nothing is §5.4's hang wearing a loop |
| `overrun` | more than one line appended (§4) |
| `pending` | a `PENDING` file is on disk: the promotion did not finish. §9's first two cases; the next session rolls it forward or clears it |
| `orphan-champion` | a `champion/R<nnnn>/` with no ledger line naming it. §8: cannot be deleted, is never the champion |
| `resume-missing`, `resume-stale` | no RESUME block, or one naming a round other than last + 1. §12.2: the ledger wins |
| `malformed-line` | a ledger line that is not a JSON object — a half-written last line |
| `duplicate-round` | the same round id on more than one line, anywhere. Round ids are one sequence across pieces (§5.5); two of one is a run that lost count, and the later file in sort order is the one the checks read |
| `snapshot-absent` | a ledgered champion directory with no `SNAPSHOT.sha256` in it, or one with no digest in it — nothing to verify against, which is not the same as verified |
| `worktree-left` | a git worktree other than the main one. §5.0 said to remove them before the round closes; the next session's RESUME line 7 is where the run learns it did not |
| `paused` | the last line is PAUSED. A session ending, not a run ending |

Every finding goes on the supervisor's ledger line as a code, and in full into
`gauntlet/relay/sessions/<n>.json`.

---

## 6. The stop gate

`stop-gate.mjs`, registered as a Grok Build `Stop` hook by `relay-stop.json`. When the agent is
about to finish its turn, the hook reads the record and either lets the turn end or hands the
reason back as a message, and the agent keeps working.

**It gates nothing unless `RELAY=1` is in its environment**, which only the supervisor sets on
the sessions it launches. Your own session, and every other project on this machine, sees a
hook that exits 0 and says nothing. That is the difference between a control and a nuisance.

**A boundary, from disk, is three things:** at least one ledger line appended since
`session.json` was written, no `PENDING` on disk — whether or not a line was appended, because
§8's boundary is the far side of step 4 — and no child still running in the harness's own
`backgroundTasks` list (one it reports finished is not a child). Anything else is a turn ending
mid-round, and the reason the model receives names which of the three it is and what §12.3, §8
or §5.4 says to do about it — finish the round and append the line; finish the promotion in
order and THEN delete `PENDING`; collect or kill each child by id.

**Two stops the method itself mandates are let through without a line, and only these.** §12.1:
the session found the record disagreeing with itself at ON STARTUP — the gate runs the same
boundary check over the same files, and a record with a fault on it is let go, because holding a
turn against a faulted record buys nothing and the supervisor stops on it next. §11: a SETUP probe
failed before round 0, the one stop the method allows before anything is built — the session
writes the reason to `gauntlet/ABORT` and ends, and the gate honours that file only while no
ledger line exists at all; afterwards it is a report, and the supervisor ends the run with exit 5
and the file's text. An `ABORT` written after round 0 is a file, not a boundary, and the gate
holds the turn as it would any other. Both let-throughs are recorded in `gate.json` and on the
supervisor's line, so a session that ended without a line is never a mystery.

**It gives up before the harness does.** Grok Build allows eight continuations per turn and then
forces the stop with no hook consulted. The gate blocks six times, writes the count to
`gauntlet/relay/gate.json`, and lets the seventh through — so that when a session does end
mid-round, the supervisor's ledger says `gate: {blocks: 7, overrun: true}` rather than the
harness having silenced a gate that was still saying no. A hook that fails, times out or emits
anything malformed is fail-open by the harness's rule, so the gate is fast (file reads, no
network, a twenty-second timeout it never approaches) and every path in it exits cleanly.

---

## 7. The doctor

`doctor.mjs`: §11's setup, performed by a script rather than a model, before round 0 spends
anything. Twenty-one checks, no LLM, no network, in the order a person would want them: Node and
`grok` on `PATH`; the repo root, a commit, the three `.gitignore` lines and the `.gitattributes`
line that keep sealed checks and reference captures out of builder worktrees and keep clean
checkouts byte-identical; the kit files; **the window check** — `PROMPT.md` says GROK BUILD, or
this kit was generated for the other CLI and has a hook this window never reads; the context
file; both hooks registered and their scripts present; the folder trusted in
`~/.grok/trusted_folders.toml`, because an untrusted project hook is skipped in silence; **the
deny hook fired with a real payload and answering exit 2 to a truncation and exit 0 to an
append**; Playwright and Chromium, without which every sealed entry answers UNRUNNABLE, which
fails nothing; free disk against the floor; **the `mkdir` → `git archive` → `tar` two-step run
for real in a temp directory**, because it exits 1 against a directory that does not exist and
corrupts under a PowerShell pipe; and the informational rows — memory, credentials, the lock,
what is already under `gauntlet/`.

`relay run` refuses to start on any FAIL. `relay doctor` runs it alone. The result is written to
`gauntlet/relay/doctor.json` when there is a `gauntlet/` to write into, whichever way it was
invoked. Playwright and Chromium are both FAIL when absent, not one FAIL and one warning: the
outcome is the same — every sealed entry that drives a browser answers UNRUNNABLE, which fails
nothing — and a check that lets a run start into that outcome is not a check.

**The hook's command is a project-root-relative path**, `node run-kit/relay/bin/stop-gate.mjs`,
in the same form as the boundary hook the kit has shipped since 4.0, whose command
`node .claude/hooks/deny-gauntlet.mjs --harness=grok` has been observed to resolve on real runs.
The guide does not state the working directory an inline hook command runs in; the form is
proven by use, not by documentation, and the first supervised session's `gate.json` is the
confirmation that it fired.

---

## 8. Caps, and where the brake reads its number

**The supervisor is the brake, and it reads the harness's own spend report.** Every session's
JSON result carries `usage.total_tokens`; where the server stamped a complete cost it carries
`total_cost_usd`, and where any call lacked one it omits every cost float and sets
`cost_is_partial`, so nothing can sum model rows into a fake bill. The supervisor records both
per session and sums them on its ledger.

Three caps, each checked **before** the session that would cross it:

- **Rounds.** The highest round id on any ledger, plus one, against the cap. Round ids are one
  sequence across pieces (§5.5), so this is the count.
- **Tokens.** The sum of `total_tokens` across sessions against the cap — and, once two sessions
  have run, the sum plus the median session against it. That is 4.3's landing rule, moved from
  the run's estimate to arithmetic: do not start a round the typical one cannot finish.
- **Dollars.** The same, and only when every session reported a complete cost. A single
  `null` disables the dollar cap for the run and the status page says so; the token cap still
  holds.

When a cap fires the supervisor stops, writes why, and **lands**: with a clean tracked tree it
checks the champion's SHA out (detached, so `git checkout <branch>` returns you); with a dirty
one it writes BLOCKED and the exact command into `STATUS.md`. §13 says a run that ends with the
wrong version checked out has handed back the one outcome the method exists to prevent, and
under Relay the run does not get to land itself, so the supervisor does.

**Three other endings, each with its own exit code.** An integrity fault (§5) — exit 2, the
fault named, the newest champion that still verifies named. Three no-progress sessions in a row
— exit 4, because a session shape that keeps producing nothing is the hang wearing a loop, and
the supervisor's `sessions/*.json` and the run's `workbench.md` are where to read what happened.
`gauntlet/ABORT` before any round (§6) — exit 5, with the session's reason.

**A cap of zero is a cap.** `--rounds 0` stops before round 0; `null` in `relay.json` is no cap.
A flag that is present and not a number — `--rounds abc`, `--budget-tokens` with nothing after it
— refuses to start, naming the value, rather than letting the file's number rule over a typo on
the one line that governs what the run spends. And where a session reported no token figure at
all, `STATUS.md` says on the token row how many did, because a sum over nothing is not a spend
figure and a cap over it is not a brake.

**`--max-turns` is a backstop, not the control.** It is set generously (six hundred by default)
and recorded per session as `num_turns`, with `hit_max_turns` when a session reached it, so you
can see whether a round is being cut short by the turn cap rather than finishing. The deadline
is the control.

---

## 9. The machine

Everything §12.4 asked the run to do about the machine, the supervisor does, because it is the
one process that is there for the whole run.

- **Ownership.** `gauntlet/relay/ownership.md` lists every process the supervisor launched, by
  PID, with when, rewritten on every spawn and every exit. It terminates only what is on that
  list, only by PID — `taskkill /PID /T /F` on Windows, the process group on POSIX — and never by
  name, because another run's Grok is somebody's open editor.
- **The lock.** `.agent.lock` at the repo root, JSON with the holding PID, the operation and a
  timestamp, for the life of the run. A live holder is respected and the supervisor exits 3
  without forcing it. A dead holder is stale: cleared, and the clearing written on stdout and
  in the record. Released in a `finally`.
- **The disk floor.** Checked before every session. Below it the run is **held, not stopped** —
  `STATUS.md` says so, the supervisor re-checks every five minutes for up to six hours, and
  gives up with exit 4 after that. §12.4 holds with no limit; Relay puts one on it because a
  process nobody is watching that waits forever for a disk nobody is clearing is a leaked lock
  with a heartbeat, and six hours is longer than any round the fleet has recorded. It cleans
  nothing itself: what is under `gauntlet/` is the run's, and the run's own boundary rules are
  what remove worktrees and clean checkouts.
- **Landing.** Whether the champion was checked out, or why it was not, is written on
  `STATUS.md`'s `landing` row as well as stdout — a console that scrolled away is not a record
  of whether §13's one named failure was prevented. A dirty tree leaves the exact command to run.
- **Interruption.** Ctrl+C terminates the live session by PID, writes `STATUS.md` as stopped by
  the operator, releases the lock, exits 130. The champion on disk is the answer, as always.

---

## 10. The record

`gauntlet/relay/` — under `gauntlet/`, so the same `.gitignore` line keeps it out of every
builder's worktree:

| File | What it is |
|---|---|
| `relay.jsonl` | the supervisor's ledger: one line per session, append-only |
| `STATUS.md` | the window: state, champion, sessions, spend against caps, the last five sessions, the last warnings. Rewritten at every boundary |
| `ownership.md` | every process launched, by PID (§9) |
| `session.json` | what the current session was told and how many lines existed when it began; the stop gate reads it |
| `gate.json` | the stop gate's block count for the current session |
| `doctor.json` | the last doctor result |
| `sessions/<nnn>.handoff.md` | the exact prompt each session was started with |
| `sessions/<nnn>.json` | the full headless result, the boundary check in full, the last 4 KB of stderr |

One line of `relay.jsonl`:

```json
{"v":"5.0-beta","session":7,"pid":41220,"started":"…","ended":"…","wall_ms":5130000,
 "sessionId":"…","exit":0,"killed":null,"stopReason":"end_turn","num_turns":212,"hit_max_turns":false,
 "tokens":1830412,"cost_usd":4.91,"cost_partial":false,
 "before":{"lines":7,"lastRound":"R0006"},"after":{"lines":8,"lastRound":"R0007"},
 "appended":1,"progress":true,"champion":{"round":"R0005","sha":"…"},
 "faults":[],"warnings":["worktree-left"],"gate":null,"verdict":"continue"}
```

`killed` is `null`, `deadline`, `silent` or `operator`. `verdict` is `continue`, `stop:once`,
`stop:cap-rounds`, `stop:cap-tokens`, `stop:cap-tokens-landing`, `stop:cap-usd`,
`stop:cap-usd-landing`, `stop:integrity-fault`, `stop:no-progress` or `stop:setup-failed`, with
the arithmetic in parentheses where there is any. `gate.let_through`, when present, is
`fault-on-disk` or `abort-before-round-0` (§6). `notes`, when present, carries what the
supervisor did on its own account before the session — so far, only clearing a stale lock. `tokens` and `cost_usd` are the harness's figures or `null`,
never `0`, for the reason §11 of the method gives: `0` reads as a cheap round.

**The run's own ledger is untouched.** The supervisor writes nothing on it and reads it only to
check it. `cost` on the run's line is still filled by the run, from `/goal status` where that is
available; where it is not — a headless session has no `/goal status` to read — the run writes
`null`, and the supervisor's line beside it has the number.

---

## 11. What changes in the kit

A Relay kit is a Grok Build kit for the Duel or the Ratchet tier with `run-kit/relay/` added and
two things different in what surrounds it. The plain Gauntlet has no ledger for a boundary check
to read and no RESUME block for a session to start from, so it does not take Relay; the
generator does not offer it there.

**Added:**

| Path | What it is |
|---|---|
| `run-kit/relay/RELAY.md` | this document |
| `run-kit/relay/relay.json` | the configuration the generator wrote from your brief: the hand-off text, your round and token caps, the turn cap, the walls, the disk floor |
| `run-kit/relay/bin/relay.mjs` | the supervisor (§3) |
| `run-kit/relay/bin/doctor.mjs` | the pre-round-0 checks (§7) |
| `run-kit/relay/bin/check-round.mjs` | the boundary check (§5) |
| `run-kit/relay/bin/stop-gate.mjs` | the Stop hook body (§6) |
| `run-kit/relay/bin/lib.mjs` | what the four share |
| `run-kit/relay/hooks/relay-stop.json` | registers the Stop hook; `START-HERE.md` copies it to `.grok/hooks/` |

**Different:** `START-HERE.md` copies one more hook file, and its last step is a command rather
than a paste — `node run-kit/relay/bin/relay.mjs doctor`, then `node run-kit/relay/bin/relay.mjs
run`. `PROMPT.md` opens with RUN THIS UNDER RELAY rather than a `/goal` line, and carries the
RELAY block (§4). The hand-off is plain text the supervisor hands each session by file, not a
`/goal` line.

**Unchanged:** everything else. The method spec, the context file, the deny hook, the five role
files, the sealed suite, the references. A Relay run is a 4.3 run; it is just not the same
session all the way through.

---

## 12. What is measured

**Nothing, on a paid run, yet.** This is the section that will fill in, and until it does the
claims above are engineering claims — this mechanism does this thing when this file looks like
that — backed by the tests in `relay/test/`, not by a run. A hundred and forty-six cases, forty-five of them written by an adversarial verifier trying to break it: every fault and
warning in §5 built on disk and named; every branch of the stop gate fed a real-shaped payload;
the supervisor loop run against a fake `grok` that appends honest lines, appends nothing, hangs
until it is killed by PID, or tampers with a champion and is caught; the doctor against a
fixture repo with each check broken in turn.

**The first measurement is the one §14.5 of the method has been waiting for**: a coupled probe
at twenty or more rounds and three or more pieces, which nobody has run because sessions died
first. Relay is the precondition for it, not a substitute. If you run one, the numbers that
matter are on `relay.jsonl`: `wall_ms` per session against the round's own `wall_ms` on the
ledger (how much a fresh context costs per round in re-reading), `num_turns` and `tokens` per
session (whether the turn cap or the budget is what is binding), how often `overrun` and
`no-progress` appear, how often the gate had to block and whether it ever overran, and whether
`killed` is ever anything but `null`.

**What the first ten sessions will tell you**, in order of how soon you will know: whether the
doctor's checks were the right ones (it refuses to start, and the reason is either true or the
check is wrong); whether a session ends at the boundary of its own accord or only when the gate
holds it (the `gate` field); whether a round fits inside `--max-turns` (`hit_max_turns`);
whether the ledger-derived deadline is sane (`wall_ms` against the deadline in
`sessions/<n>.handoff.md`); and whether re-reading `PROMPT.md` every session is a cost worth
its size — which is a question about the prompt, not about Relay, and the one the method's own
§14.5 already raised.

---

## 13. What this does not fix

- **A vague bar.** Still the number one failure, and a supervisor makes it cheaper to run for
  longer against one.
- **The round itself.** Every death inside a round — a judge that never returns, a slate that
  cannot show the change, a builder that never ran what it built — is §5.4's and §7's business
  and unchanged. Relay makes the round's *ending* cheap. It does not make the round good.
- **The re-read.** A fresh context reads `PROMPT.md` every round, and `PROMPT.md` is large.
  §12.2 measured the RESUME block making the rest of the re-read a page; the prompt is the part
  it cannot shrink. Relay pays that price once a round rather than once a death, and records it
  in `tokens` so you can see what it costs.
- **An adversary.** `check-round.mjs` detects accidents. A run that wanted to hide a change from
  the snapshot would rewrite `SNAPSHOT.sha256` too, and the hook that denies writes under
  `gauntlet/champion/` is the same hook 4.3 has, with the same open questions §10 lists.
- **Grok Build's own limits.** Whether a headless session honours project hooks identically to
  an interactive one, what `stopReason` reads when `--max-turns` fires, and whether memory
  injection reaches subagent sessions when it is on — each is either documented and relied on,
  or not documented and defended against (`GROK_MEMORY=0` is set regardless), and none has been
  observed on a paid run. `sessions/<n>.json` keeps the raw result so that when one of them turns
  out to matter, the evidence is already on disk.

---

*Relay is an edition of the Ratchet Loop, which builds on the Gauntlet Loop by Matt Shumer. Prior
art credited in §2 here and §2 there.*
