# Artifact — the DO NOT STOP contract

**What it guarantees, and exactly where the guarantee is bound.**

## The claim, restated precisely

Repeating "do not stop" inside a prompt is a *request to the model*. It does not change what
the *runtime* does when the model attempts to end a turn. DO NOT STOP is the mechanism that binds
the runtime event instead: it intercepts the **Stop lifecycle event** — the point at which a
turn-end becomes observable to the harness — and refuses the termination when the work the session
is holding is not yet done.

The boundary is therefore not "the model decides to keep going." The boundary is **the harness
refuses to let the turn end**. That relocation of the enforcement point — from prompt text to a
lifecycle hook — is the whole idea.

## What is guaranteed

A session that holds a live work obligation cannot *silently* end a turn. Concretely, on a Stop
attempt the mechanism blocks (harness exit code `2`, which the harness treats as "refuse turn-end
and feed the message back") when any of the following holds:

- **Open items remain (P1).** Declared or discovered work items are still open. This is the
  structural load-bearing predicate — it catches *dropped list items* and *false completion on
  bounded work*, because the item ledger, not the model's self-report, decides done-ness.
- **The turn-ending message contradicts the act of stopping (P2 / rule R1).** The message asserts
  continuation ("225 cells remain … continuing down the list") and then stops. The rule keys on the
  **contradiction**, not on interrogative grammar — which is why it catches the witnessed real case
  that contained no question mark and so could never have tripped a question-based gate.

Release is *always reachable by construction* (the mechanism is wedge-free): discharge an item with
evidence, waive an item with a reason, escalate on a closed three-class enum (defined by
irreversibility, not by uncertainty), or — when the remaining work is genuinely blocked on external
completion — arm a self-firing wakeup "return ticket" so the turn may end and the session is
guaranteed to resume. A circuit breaker degrades the gate to advisory after 3 consecutive
no-progress re-entries; a kill switch (`FOUNDRYOS_SESSION_CONTRACT=0`) disables it outright.

## Where the boundary is bound (real sources)

Two sibling Stop-hook gates bind the same lifecycle event for the two session classes; exactly one
ever decides (they mutually stand down):

| Session class | Gate script (verified) | Release predicate |
| --- | --- | --- |
| Ordinary operator session (armed "keep going" contract) | `system/_operator/scripts/check-session-stop-contract.mjs` | open items = 0 **and** no self-contradicting stop message |
| Chassis / `auto` audit-loop run | `system/_operator/scripts/check-audit-loop-terminal.mjs` | the owned run reached a terminal run-state (`closed` / `elevated`) |

- **The block signal.** `check-session-stop-contract.mjs` defines `EXIT_BLOCK = 2` and returns it
  with the denial rendered on stderr (`runStopGate`, ~L66 / ~L319). `check-audit-loop-terminal.mjs`
  states the contract inline: *"Exit 2 is the Stop-hook BLOCK signal (Claude Code feeds stderr back
  + refuses turn-end)"* (~L232).
- **Registration as a real `Stop` hook.** The ordinary-session gate is installed into the root
  `.claude/settings.json` under the `Stop` event by
  `system/_operator/scripts/install-session-contract-hooks.mjs` (~L66:
  `Stop: [{ hooks: [{ type: 'command', command: … 'check-session-stop-contract.mjs' }] }]`). The
  chassis gate is registered under `Stop` by the two-signal-surfacing installer and audited by
  Caliber C168.
- **Doctrine.** `docs/OPERATING_STANDARDS.md § Session Work-Contract Surfacing Enforcement`
  (ordinary sessions) and `§ Two-Signal Surfacing Enforcement` (chassis runs).

## What the mechanism does NOT claim

It makes stopping **expensive and recorded, not impossible** — a determined model can still find an
exit, and the mechanism is a forcing function, not a proof of task completion. See
`field-note-link.md` for the published Field Note's own honesty boundaries, and the package
`README.md` for this package's limitations.
