# F5 r2 — the revised step table

hertz, 2026-09-13. **PROPOSAL ONLY.** `fp-driver-d2.sh` (e093e80d) is frozen and unedited;
`d2_capture.ps1` 69b8506a is frozen. Nothing here is applied. This REPLACES
`F5-BOUNDED-STEP-PROPOSAL.md` (superseded by doyle's ruling CT6GS2Y6) — it is a rewrite, not a
patch, and the superseded file is kept beside it unaltered except for a pointer line.

## 0. What I withdraw, in my own words

1. **"Once every step carries an honest enforced bound, the activity set does not fit in 300
   seconds."** Withdrawn. It was not established by the evidence I had: the six operations inside
   the two unmeasured spans were never individually stamped, so I had no durations to sum, and
   doyle's correction is the one I had backwards — **an enforced bound LIMITS work; it does not ADD
   duration.** A bound cannot make a sequence longer than it was.
2. **Moving the post-setup census, the identity capture and the state-dependent D1 execution to
   before GO.** Withdrawn. **A baseline cannot replace a post-mutation observation**: those three
   exist to say what the setup DID, and a reading taken before the mutation answers a different
   question. §2 shows each dependency explicitly instead of moving it.

Retained per the ruling: **300s activity, 180s cleanup/reporting. No budget increase. No
instrument-first field run.** No step below is justified by a duration estimate, and none is
needed: every bound in the table is `deadline − now`.

> **r2.1 (this file, revised in place against doyle O2VBV5LC).** Deferring t1/t2 validation, the
> 120s handoff allocation and keeping the D1 render with its consumer are ACCEPTED and unchanged.
> Five gaps are repaired here, in the sections they belong to and nowhere else: §5 R6 withholds
> **every** cleanup mutation and defines what proves the setup finished; §1 and §6 stop treating
> `timeout -k 5` as proof a native Windows child stopped, and put the 5s escalation inside the
> deadline; §4 adds the launched-child register, because A11's sample predates A14's child; §1/§5
> reserve the reporting interval so R10 cannot be starved; §4/§6 correct "timed out" to
> **completion unverified**, with partial artifacts preserved and inspected.
> **r2.2 (doyle B3CMWRQI):** every wait/termination/descendant query is bounded by
> `WORK_DEADLINE` and yields `UNREADABLE`/`OUTSTANDING` rather than a delay (§1); the register
> distinguishes the msys wrapper from the native child and reports `ATTRIBUTION_INCOMPLETE`
> for a child that exited before registration (§4a); a NONZERO native exit is a COMPLETED
> outcome and timestamp ordering joins the completion conjuncts (§5a).

## 1. The deadline instants, the kill grace, and the reporting reserve

The driver today has two budgets and computes *remaining seconds* on demand (`exp_left`,
`cleanup_left`). The revision keeps both budgets and adds the **instants** they imply, so that every
step's bound is derived from a fixed deadline rather than recomputed from a duration:

    at go()               EXP_DEADLINE    = RUN_EPOCH     + 300      (activity)
    at end_experimental() REPORT_DEADLINE = CLEANUP_EPOCH + 180      (the report is DUE)
                          WORK_DEADLINE   = REPORT_DEADLINE - LEDGER_RESERVE_S
    constants             LEDGER_RESERVE_S = 30            KILL_GRACE_S = 5

**The reporting reserve (doyle 4).** R10 cannot promise an on-time report if R1..R9 may consume the
whole 180s, and every one of them could. So **every cleanup step except the ledger is bounded by
`WORK_DEADLINE`, not by `REPORT_DEADLINE`** — the last 30s belong to the ledger and to nothing else.
The reserve is a policy allocation like the handoff cap (§3), not an estimate of how long a ledger
takes; it is the answer to *how much of the reporting window is spent reporting*. And the ledger
**emits without waiting for any child to terminate**: an un-reaped child is written into the report
as an OUTSTANDING identity, which is a line to print, not a thing to wait on.

**Every query is inside the budget too (doyle B3CMWRQI).** The bound applies not only to the step's
own child but to **each query the disposition machinery makes** — the handoff waits, the
termination-status re-query, and the descendant walk. Each is `bounded()` against `WORK_DEADLINE`,
and a query that cannot answer inside it **yields a value, not a delay**: `UNREADABLE` for an
identity that could not be re-queried, `OUTSTANDING` for a child whose termination is unknown.
**The ledger never depends on one of these returning** — it prints whatever value each query left
behind, including the two above, and R10 owns its reserved interval regardless.

**The escalation is inside the deadline (doyle 2).** `timeout -k 5` sends TERM at the bound and KILL
five seconds later, so a bound of `deadline − now` overruns its own deadline by up to
`KILL_GRACE_S`. Every bound is therefore **`(deadline − now) − KILL_GRACE_S`, floored at 1s**, and a
step whose remaining budget cannot cover the grace is **not started** — recorded TRUNCATED, which is
NOT CAPTURED and not "not present".

A step's **absolute deadline** is one of those instants, or an earlier one where a POLICY ALLOCATION
applies (§3). **No estimated duration appears anywhere in the design**, which is what makes the table
below writable from a frozen run that stamped only two budget reads.

## 2. Which host-free work may move, and the dependency for each

Doyle approved deferring host-free validation/rendering **only where no in-window decision consumes
its result**. Traced in the frozen driver, by consumer:

| host-free step | its result | every consumer | verdict |
|---|---|---|---|
| `d2_validate_at t1` (:2334) | `D2_T1_VALID`, `D2_T1_QF` | `d2_ledger` only — :1562 (`t1v`), :1568 (`qf` sum), :1587 (printed). `d2_ledger` runs in cleanup. | **DEFER** to reporting |
| `d2_validate_at t2` (:2419) | `D2_T2_VALID`, `D2_T2_QF` | same three lines, same function | **DEFER** to reporting |
| `d1_render.py` (inside `d1_capture`, :2008) | `$R/d1-<tag>.b64` | the **host snapshot in the same function** (:2020) passes it as `-EncodedCommand`, and the A2.1 pre-check on its stdout (:2017) gates that snapshot | **KEEP IN WINDOW** |
| `face_report` / `pair_state` / `binder_path_verdict` | `setup_face`, `pair`, binder verdict | recorded in-window (:2365) beside the observation they qualify | **KEEP** — they read files the in-window observations just produced |

`d2_validate_at t3` is already in cleanup (:1281), so deferring t1 and t2 puts all three validations
in one place and removes the last host-free work from the activity window.

**The render is the case the "it touches no host" test gets wrong.** It is host-free and it is still
not movable: its output is the command the post-mutation snapshot runs. Moving it before GO would
mean rendering a snapshot command for a host state that does not exist yet, and the thing being
deferred is not the cost — it is the *ordering*.

**Not moved, and why (the three doyle named):** `census_at setup-post` (:2353), `capture_run_identities
post-setup` (:2408) and the D1 execution (:2415) each observe the state the elevated setup produced.
`guard_assert post-setup` (:2347) and `port_at setup-post-listener` (:2389) are the same class. Each
has a pre-GO counterpart already taken (`census_at pre-setup-absence` :2060, the 5470 guard baseline,
`PRE_SETUP_ABSENCE`) — **that pair is the measurement**, and one half of it cannot stand in for the
other.

## 3. The handoff cap — a policy allocation

`handoff_await` is bounded today by `exp_left`: one slow counterparty can consume the whole activity
window, and in run 20260913T041410Z it did (134s, reaching neither t1 nor t2).

**Proposed allocation: `HANDOFF_CAP_S = 120`, effective bound `min(HANDOFF_CAP_S, EXP_DEADLINE − now)`.**

This is a POLICY ALLOCATION, not an estimate, and I want it read that way: it is the answer to *how
much of the 300s this run is willing to spend waiting on another agent*, not a prediction of how long
liam takes. The two samples I have (94s, 134s) are not a distribution and I am not deriving the number
from them. What the allocation buys is stated instead: **it reserves the remainder of the window for
the observations that can only be taken inside it.** If doyle wants a different split, the number
changes and nothing else in the design does.

### If the cap expires WHILE THE EXECUTOR IS ALREADY ACTING

This is the case the cap must answer, because a receipt's absence does not mean the command did not
run. What the driver does:

1. **Stops waiting. Kills nothing, dispatches nothing.** There is no termination authority over an
   elevated leg this driver never entered, and asking for one is not in scope.
2. **No second request and no second nonce.** One nonce is one execution; a re-ask is a second
   authorization, not a retry.
3. Records `HANDOFF_CAP_EXPIRED` naming the outstanding nonce, the request path and the receipt path,
   and leaves `fw_mutation_may_have_occurred`, `daemon_may_run` and `listener_may_exist` **set**.
4. **Does NOT proceed into the post-mutation observation set.** Those observations would be taken
   while a mutating command may still be writing, and would be recorded under names
   (`census setup-post`, `t2`) that assert they are *after* the setup. That is a falsified
   observation, which is worse than a missing one.
5. `end_experimental "setup handoff cap expired with the command outstanding"` → the reporting clock
   starts and cleanup is entered through the EXIT trap, exactly as any other truncation.
6. **Cleanup must not race the outstanding command** (§5 R6): **every** cleanup mutation is
   WITHHELD — the listener stop, the rule teardown and the daemon stop, not the teardown alone —
   and the report is emitted on time as PENDING.

## 4. The activity window — revised step table

Every row's **absolute deadline** is an instant; every **timeout mechanism** says what it stops.
`bounded()` is the one proposed helper, and it does four things, not one:

1. computes `(deadline − now) − KILL_GRACE_S`, refusing at ≤0 with the phase recorded TRUNCATED —
   **NOT CAPTURED, never "not present"**;
2. **registers the child it is about to spawn** in a run-bound LAUNCHED table (§4a) before running
   it under `timeout -k 5 <bound>`;
3. on expiry, **measures what actually stopped** — re-queries the registered pid and every
   descendant it can still see, and records `TERMINATION=CONFIRMED_GONE | SURVIVING | UNREADABLE`
   with the identities. It never reports a kill it did not measure, and **never infers "nothing to
   dispose" from the fact that the command was read-only**: a read-only command still leaves a
   process, and on Windows `timeout`'s TERM is not the signal a native child is obliged to obey;
4. **preserves whatever the killed step wrote**, under a name that says it is unverified (§6).

One helper, every host-touching child, instead of a per-call-site bound.

| # | phase (line) | inputs | host effects | absolute deadline | timeout mechanism | outstanding-process disposition |
|---|---|---|---|---|---|---|
| A0 | `deadline_check elevated-setup` (:2178) | `EXP_DEADLINE` | none | EXP_DEADLINE | none needed (a clock read) | n/a |
| A1 | `guard_assert pre-setup` (:2179) | 5470 guard baseline (pre-GO) | **READ** 5470 rule fields + listener identity (`portfields.ps1`) | EXP_DEADLINE | `bounded()` → `timeout -k 5`, **attempts to stop the operation** (our own child) | phase TRUNCATED, **and the termination outcome is measured, not assumed**: registered pid + descendants re-queried, `CONFIRMED_GONE / SURVIVING / UNREADABLE` recorded. A read-only command still leaves a process |
| A2 | `mark_state` ×3 (:2186-2188) | — | none (local state files) | EXP_DEADLINE | none needed | markers stay set for cleanup **by design** |
| A3 | `handoff_request setup` + t1 rider (:2213) | nonce, `EXE_SHA`, `CAPTURE_SHA`, rule names, port, `$H` | **none by this driver** — it writes a request file | EXP_DEADLINE | none needed (a file write) | after this line rules may exist **even if the setup is refused** (A2's markers) |
| A4 | `handoff_await trial` (:2276) | receipt path, nonce | **the executor mutates** (`serve lan --bootstrap`: firewall pair, daemon, listener) + takes t1 | **min(now+120, EXP_DEADLINE)** — §3 | poll loop with an expiry; **stops the WAIT only. No authority over the elevated leg** | on expiry: §3 — markers set, no kill, no re-ask, activity ends, cleanup withholds teardown |
| A5 | leg read-back, `TS_ORDER` (:2295-2331) | 6 stamp files, `setup.exit` | none (local file reads) | EXP_DEADLINE | none needed | absent file = labelled gap, never "now", never the neighbouring stamp |
| A6 | ~~`d2_validate_at t1`~~ | — | — | — | — | **DEFERRED to R2** (§2) |
| A7 | `guard_assert post-setup` (:2347) | A1's baseline | **READ** 5470 | EXP_DEADLINE | `bounded()`, attempts to stop the operation | as A1 — termination outcome measured and recorded. A diff here is `die`; cleanup still runs via the EXIT trap |
| A8 | `census_at setup-post` (:2353) | `census.ps1`, tag | **READ** — full rule enumeration (1001 rules in the field run) | EXP_DEADLINE | `bounded()`, attempts to stop the operation | a killed census is an UNMEASURED pair state, never an absent pair; its partial `census.log` lines are kept and the tag is marked unverified; termination outcome measured |
| A9 | `face_report`, `pair_state setup-post` (:2361-2364) | `SETUP_OUT`/`ERR`, A8's log | none (local reads) | EXP_DEADLINE | none needed | — |
| A10 | `port_at setup-post-listener` + `binder_path_verdict` (:2389) | `portfields.ps1`, `$EXE` | **READ** 29470 fields + listener identity | EXP_DEADLINE | `bounded()`, attempts to stop the operation | on expiry: binder UNMEASURED — an instrument failure, **not a finding about the binder**; termination outcome measured |
| A11 | `capture_run_identities post-setup` (:2408) | `$H/daemon.pid` | **READ** — `Get-CimInstance Win32_Process` enumeration | EXP_DEADLINE | `bounded()`, attempts to stop the operation | **this is the step cleanup depends on**: a killed capture ⇒ residue UNREADABLE, never CLEAR, and `CLEANUP=VERIFIED` is then unreachable by construction. **It samples the host as it stands HERE, so it cannot cover anything A13/A14 launch later** — that is §4a's job |
| A12 | `deadline_check t2-capture` (:2413) | `EXP_DEADLINE` | none | EXP_DEADLINE | none needed | n/a |
| A13 | `d1_capture setup-outcome` (:2415) — render **then** exec | `d1_render.py`, `SUBJECT_BLOB`, the render's `.b64` | render: none. exec: **READ** (out-of-band snapshot) | EXP_DEADLINE, **one deadline across both stages** | `bounded()` on each stage, both children registered (§4a) | render killed ⇒ `D1_FAILED`, capture **discarded, not patched up** (:2013). Exec killed ⇒ `D1_FAILED` and the partial `d1-<tag>-snapshot.json` is **preserved unread** under an unverified name — not read, not deleted. Termination outcome measured for both |
| A14 | `d2_capture_at t2` (:2416) | 69b8506a, rule names, port, `$H`, setup interval | **READ** (NetSecurity/CIM) | `min(EXP_DEADLINE, now+180)` — the plan's per-point cap, already in the driver, **less `KILL_GRACE_S`** | `timeout -k 5` **already present** (:887), now via `bounded()` so the child is registered | on expiry: **CAPTURE_COMPLETION=UNVERIFIED** — the point is not captured, and that is a statement about the OBSERVATION, never about the disk. Whatever is at `d2-t2.json`/`.b64` is **preserved under an unverified name, sized and hashed, and inspected later** (§6). The survivor is found through §4a's registered identity, **not** through `-IsolatedHome $H` on a command line |
| A15 | ~~`d2_validate_at t2`~~ | — | — | — | — | **DEFERRED to R3** (§2) |
| A16 | `end_experimental` (:2433) | — | none | — | — | starts `REPORT_DEADLINE`; **idempotent**, so a truncation and a clean finish start the clock exactly once |

## 4a. The launched-child register — why A11 cannot cover A14

`capture_run_identities post-setup` (:2408) samples the host **at :2408**. A13 and A14 spawn children
*after* it, so a t2 capture that outlives its bound is **not in A11's rows**, and cleanup's re-query
(R9) walks exactly those rows. The frozen driver's own note at :895 says a survivor is counted
"BY CONSTRUCTION" because it carries `-IsolatedHome '$H'` on its command line — **that is a string in
a command line, and this driver has already been burned by exactly that predicate**: the r10
survivors ran `daemon run --detached` and `daemon brain --generation 0 --start-reason cold`, neither
carrying the home, which is why :1202's command-line list is documented as known-incomplete and kept
only as supplementary evidence. **A home string is not attribution.** I withdraw that clause of my
A14 row.

What replaces it: **`bounded()` registers every child it launches, at launch.**

    LAUNCHED  role=<step-label>  pid=<the spawned pid>  created=<its creation instant>
              exe=[<ExecutablePath>]  cmd=[<CommandLine>]  parent=<driver pid>

- Written **before** the child can finish, so a child that runs away is already on the record.
- Same schema and same comparison as the S9a identity rows, so R9 re-queries LAUNCHED rows and
  IDENT rows with one predicate — **pid plus creation instant**, because pid reuse on this box is
  measured and a pid answering with a different creation time is a different process.
- Descendants: at registration the driver records the child; at expiry (and again at R9) it walks
  the process table for anything whose parent chain reaches the registered pid **at that moment**
  and adds those rows. A descendant the table no longer attributes is `UNREADABLE`, never absent.
- A LAUNCHED row that cannot be re-queried makes the run's residue **UNREADABLE**, which is never
  CLEAR and blocks `CLEANUP=VERIFIED` — the same rule the IDENT rows already carry.

**Two processes, not one, and the register says which is which (doyle B3CMWRQI).** `bounded()` spawns
the msys `timeout` wrapper; the wrapper spawns the native `powershell.exe`/`pwsh.exe`. They are
different processes with different pids and different termination behaviour — the one the shell
hands back is the WRAPPER, and the one that may survive is the NATIVE CHILD. Each row therefore
carries `kind=wrapper|native|descendant`, and a disposition that names only the wrapper has not
spoken about the child at all.

**A child that exited before registration is NOT confirmed gone.** Registration is a race it can win:
a fast child can be gone from the process table before the register writes its row. That produces
`kind=native state=ATTRIBUTION_INCOMPLETE`, carrying what evidence does exist — the wrapper's launch
handle and the exit status the wrapper reported — and **never** `CONFIRMED_GONE`. An absence at the
moment of registration is an absence of a reading, not a reading of an absence; the two are only the
same when a registered pid is re-queried and measured absent.

## 5. The reporting window — revised step table

| # | phase (line) | inputs | host effects | absolute deadline | timeout mechanism | outstanding-process disposition |
|---|---|---|---|---|---|---|
| R0 | `cleanup` entry (:1235-1236) | state markers | none | WORK_DEADLINE | — | entered through the EXIT trap on **every** path, deadline included |
| R1 | `d2_capture_at t3` (:1278) | as A14 | **READ** | `min(WORK_DEADLINE, now+180)` less `KILL_GRACE_S` | `bounded()` — child registered (§4a) | as A14: completion UNVERIFIED on expiry, artifacts preserved unread, termination outcome measured |
| R2 | `d2_validate_at t1`, `t3` (:1281) | t1/t3 documents | none | WORK_DEADLINE | `bounded()` | A6 lands here |
| R3 | `d2_validate_at t2` | t2 document | none | WORK_DEADLINE | `bounded()` | A15 lands here |
| R4 | manifest `sha256sum` (:1284) | the six documents **plus any preserved-unverified artifact** | none | WORK_DEADLINE | `bounded()` | hashed **before** the teardown, deliberately; an unverified partial is hashed under its own name, never under the point's |
| R5 | `guard_assert cleanup-pre-teardown` (:1287) | baseline | **READ** 5470 | WORK_DEADLINE | `bounded()` | status recorded; cannot exit from cleanup (:1073) |
| R6 | **outstanding-command gate (new, revised — §5a)** | the setup receipt's *terminal fields* and the leg's return stamp | none | WORK_DEADLINE | a single **read**, not a wait | **if setup completion is not PROVEN, every cleanup mutation is withheld** — the listener stop, the teardown and the daemon stop — each recorded `WITHHELD_OUTSTANDING_SETUP` with the nonce and the rule names, `CLEANUP_VERDICT=PENDING` |
| R7 | `handoff_request teardown` + `handoff_await cleanup` (:1337-1347) | pinned removal payload (verified twice), **R6 passed** | **the executor removes** the two owned rules from PersistentStore | WORK_DEADLINE (`CLEANUP_HANDOFF_WAIT_S = WORK_DEADLINE − now`) | expiry **stops the WAIT only** | on expiry: `PENDING/review`, **not retried**, state marker left set deliberately |
| R8 | `census_at cleanup-teardown-post` (:1355) + independent per-name/per-store removal check | census, per-name probe | **READ** | WORK_DEADLINE | `bounded()` | `TEARDOWN_VERIFIED` needs zero remover exit **and** a valid census **and** an independent absence reading; anything else is PENDING |
| R9 | residual-identity re-query (:1116-1198) | A11's IDENT rows **and §4a's LAUNCHED rows** | **READ** process table | WORK_DEADLINE | `bounded()` | **UNREADABLE is never CLEAR.** A killed re-query leaves UNREADABLE, and `CLEANUP_STOP_WITHHELD` follows — no stop is issued on an unreadable measurement (IR-124) |
| R10 | `d2_ledger` | every verdict above | none | **REPORT_DEADLINE — and it owns the reserved `LEDGER_RESERVE_S` alone** | — | **emitted on time, with PENDING in it, WITHOUT waiting for any child to terminate.** An un-reaped child is reported as an OUTSTANDING identity. The deadline means the report is due; it never converts PENDING into VERIFIED |

## 5a. R6 in full — what an outstanding command withholds, and what proves it finished

My first R6 guarded rule removal only. That was too narrow: **cleanup has three mutating steps and a
fourth local one**, and an outstanding elevated setup races all three.

| # | cleanup mutation (line) | what it changes | gated by R6? |
|---|---|---|---|
| 1 | `serve lan --stop` (:1262) | stops the listener — **and it is AUTOSTARTING**: it reaches the broker through `ensure_running` (serveverb.rs:191) and is how IR-124 manufactured supervisor 45944 / brain 22524 | **YES** |
| 2 | the teardown handoff (:1330) | an elevated leg removes both owned rules from PersistentStore | **YES** |
| 3 | `daemon stop` (:1423) | stops the isolated daemon | **YES** |
| 4 | `rm -rf "$SECRET"` (:1494) | removes `$R/.secret` | **no** — it is this run's own directory, created at :295 and referenced at exactly those two lines, so nothing else reads or writes it and there is nothing to race |

Steps 1 and 3 already have their own gates — *marker asks the question, measurement decides the
command* (IR-124) — and those stay exactly as they are. **R6 sits in front of all three**, because
those gates answer "is there something to stop", and R6 answers a different question: **"is someone
else still acting on this host?"** A measurement taken while an elevated leg is mid-reconcile can
read LIVE or GONE for reasons that have nothing to do with what this run left behind.

### What counts as PROOF that the setup finished

**Presence of a receipt file is not it.** The proof is the run-bound completion receipt *and* its
terminal fields, all four:

1. the receipt file exists at `$HO_RECEIPT`, and
2. it carries **exactly one** line `nonce: <this request's nonce>` — the whole-line match
   `handoff_await` already requires (a substring quote is not a receipt), and
3. it carries **exactly one** whole-line `^exit=[0-9]+$` record — i.e. `handoff_exit` returns
   something other than `255`, because an unread exit is not a zero one, and
4. the leg's own terminal stamp `setup-return.utc` is present and **parseable by the calendar**
   (`ts_readable`) — the stamp the request asks for *after* the command returns. Where the t1 rider
   was requested, `d2-t1-end.utc` must satisfy the same test.

**A NONZERO exit is a COMPLETED outcome (doyle B3CMWRQI).** The test is whether the leg FINISHED,
not whether it succeeded: `exit=1`, `exit=255` from the product, a refusal, a crash the leg reported
— each is a command that ran and returned, and each satisfies conjunct 3. **Failed execution is not
outstanding execution**, and treating it as outstanding would withhold cleanup from exactly the runs
that most need it (a refused setup can still have left rules — that is why the markers are set before
the dispatch). The setup's exit keeps its own meaning in the ledger, four separate values, none
inferred from another.

5. **Ordering is validated, not assumed**: the six stamps are compared with the calendar exactly as
   `TS_ORDER` already does (:2318-2331) — `handoff_request ≤ setup_start ≤ setup_return ≤ t1_start ≤
   t1_end ≤ handoff_receipt`. `OK` supports completion; `VIOLATED` or `UNMEASURED` leaves it
   UNPROVEN, because a record whose own order cannot be established cannot establish when the leg
   stopped.

Anything else — absent, unparseable, two exit records, a receipt with no exit record at all, or an
ordering that is not `OK` — is `SETUP_COMPLETION=UNPROVEN`, and **unproven is treated as
outstanding**. That is deliberately
conservative: a leg that finished but wrote no terminal field is indistinguishable from one still
running, and the cost of being wrong in that direction is a PENDING report, while the cost in the
other direction is two elevated commands mutating one rule store with no ordering between them.

R6 **reads once and does not wait**: a receipt that arrived late is evidence to read, never a reason
to spend reporting budget waiting. If the read proves completion, R7-R9 proceed normally.

## 6. The four distinctions this table is built on

**1. Stopping the wait is not stopping the operation.** Two mechanisms appear above and they are not
interchangeable:

- `timeout -k 5` acts on **a child this driver spawned**. It attempts to stop the operation.
- A handoff expiry acts on **this driver's patience**. It stops the wait. The elevated leg keeps
  running, and nothing here asks for, implies, or acquires the authority to stop it. **A command whose
  bounds this driver cannot enforce can only be bounded where it runs** — in the request text, by the
  leg's own stamps — and that is a request, not an enforcement.

**2. A bound expiring is not a termination.** `timeout` runs under msys and sends TERM, then KILL
five seconds later, to **the child it launched**. A native Windows child is under no obligation to
answer TERM as a POSIX process would, and its **descendants are in no job object** that the kill
reaches. So the expiry of a bound establishes only that *this driver stopped waiting on that child*.
What actually stopped is a **measurement**: the registered pid and its descendants re-queried after
the escalation, recorded `CONFIRMED_GONE / SURVIVING / UNREADABLE`. I withdraw the "killed child = a
read; nothing to dispose" disposition from my first A1 row — **read-only describes what a command
asks the host, not what its process does afterwards**, and a survivor of a read-only probe is a
survivor. The escalation interval is inside the deadline (§1), so TERM+KILL completes by the deadline
rather than five seconds past it.

**3. Completion unverified is not an absent document.** This is the exit-2 correction again, in the
timeout direction. A capture whose bound expired may have written **nothing, a partial document, or a
complete one that was never read back** — the driver cannot know which without looking. So the
disposition is: **CAPTURE_COMPLETION=UNVERIFIED** (a statement about the observation), the artifact
**preserved under a name that says it is unverified**, sized and hashed into the manifest under that
name, and **inspected afterwards** rather than assumed. Nothing at the point's own path is deleted,
overwritten or reused, and no absence is claimed.

**4. Cleanup must not race an outstanding mutating command.** R6 exists for exactly that (§5a):
while the setup may still be acting, dispatching a removal is two elevated commands mutating one
store with no ordering between them — and a listener stop or a daemon stop is the same race against
the processes that leg is starting. The driver withholds **all three**, names what is outstanding,
and reports PENDING on time. **A run that cannot verify its host is tidy says so**; it does not act
to make the table look tidy.

## 7. Owed when the driver is next opened (not proposed here, carried so it is not lost)

`d2_capture_at`'s record line (:900) still reads *"0=document written, 2=could not write,
3=arguments rejected"* and does not know exits **4** (written, receipt digest not takeable) or **5**
(read-back digest disagrees), which 69b8506a can now return. A 4 or a 5 would reach the ledger as an
unlabelled nonzero.

## 8. What this proposal does not ask for

- No budget change. No instrument-first field run.
- No driver edit, host query, field run, signing, apply, build or daemon launch — this is a document.
- No automatic termination authority over anything this driver did not spawn.
- No change to the truncation semantics: the deadline ends activity, it never skips cleanup, and a
  truncated phase reports **NOT CAPTURED**, which is not the statement "not present".
