# releases#196 — the precise-allow write contract (todlando, PORTER wave 3)

STATUS 2026-08-21: RULED, BUILT. Lane `.worktrees/fix-196`, branch
`fix/196-precise-allow-write-contract`, base `b88fab2a`, pool claimed for
lane `fix-196`. UNPUSHED.

The census that preceded this is `W3-196-CENSUS.md` in the main tree (7
findings, source-only at `b88fab2a`). doyle ruled from it: **HORN B WITH THE
DIAGNOSTIC**, candidate accepted, no operator gate needed (horn B retains the
shipped T6 semantic and adds a notice — horn A was the behavior change that
would have needed one).

## The ruling, as it binds this lane

- The precise arm is a RULE verb; the positional arm is a RULE+POSTURE verb.
  Finding 3's framing was ADOPTED into the ruling.
- The mutation seam stays POSTURE-BLIND. Findings 2 and 4 are structural: a
  close cannot live at the seam without firing on knock-answer, redeem and the
  engine room's `--for` seat, and it would falsify the grant-nodes policy's own
  "punches no hole" premise inside the very command that policy gates.
- The docstring horn was already discharged by `f2d215a5` (finding 1) — closed
  as already-done, not re-stated.
- Sub-questions ruled by doyle: (a) an absence is never a verdict — an empty
  subnet gets a NAMED ABSENCE, never "inert"; a subnet with members is inert
  only if EVERY member reads inert; (b) NO notice on deny this lane — deny is
  the fail-closed direction and the narrowed-deny shape is un-censused; (c) REQ
  first, one new id covering the whole write-time contract.

## What shipped

**REQ**: `REQ-ACL-PRECISE-ALLOW-WRITE-CONTRACT`, minted and activated in this
lane at `impl`+`unit`+`int`. No doc stage, and the reason is recorded on the
entry: the operator-facing half is `f2d215a5`'s per-spelling docstring, which is
already correct.

**impl** (`crates/spt/src/cli.rs`):

- `inert_notice_population` — the firing population as a PREDICATE, not as a
  consequence of where the call sits. Operator-driven precise ALLOW upserts on
  an endpoint scope, and nothing else: the ER `--for` seat is excluded by the
  seat flag its caller already passes, removals and denies by op and decision,
  and knock-answer/redeem never reach this body at all (they apply through the
  store seam one level below).
- `grant_origins` + `origin_node_of_endpoint` — the subject resolved to the
  concrete machines this node can ask the chain about, with the endpoint
  resolver passed IN so the rule about an unresolved subject is testable without
  a registry on disk.
- `inert_grant_with` — the verdict, ASKED OF THE CHAIN (`decide()` per covered
  surface, per admitted origin class, per origin), never re-derived from rule
  rows. Asked BEFORE the write, so "already allowed" means what the chain said
  without this rule in it.
- `inert_grant_line` — the sentence. Never a refusal. It names the only lever
  that actually exists: there is NO operator verb that closes an endpoint's
  posture on its own, so it names the positional spelling's v1 close.
- The call site inside `tuple_mutation`, before `apply_mutation`, printed after
  the acceptance line (releases#163 doctrine) and first among the three notices.

**unit** (`cli.rs` tests, 3 cells):

1. `the_inert_notice_speaks_only_for_the_operators_own_precise_allow` — the
   population table, six rows, expectations written out as literals.
2. `the_inert_grant_verdict_follows_the_chain_not_the_endpoints_posture` — the
   verdict table including THE HOLE-PUNCH negative control (a grant shadowing a
   node-scope deny must read NOT inert), partial-surface coverage, the per-class
   arm, and a split subnet.
3. `a_subject_with_no_origin_gets_a_named_absence_and_never_an_inert_verdict` —
   the named absence, including the point-of-use guard on an empty concrete
   population, and the line assertions that the absence never borrows the
   verdict sentence.

**int** (`crates/spt/tests/access_precise_allow_e2e.rs`, one binary, five arms):
the precise arm leaves the posture EMPTY (asserted as two raw fields, a literal
partition — never `Modes::is_empty()`, which is the predicate the production
code uses) and prints the notice; the positional sibling in the same home still
closes and stays silent; the hole-punch stays silent; the named absence renders;
and THE NEGATIVE POPULATION PIN — a rule written through the invite-code
redemption path (`redeem_local` → `apply_mutation`, the same seam knock-answer
takes) onto an endpoint with NO posture, so its rule is inert by exactly the
reading arm 1 fires on, prints no notice at all.

## Traps hit and avoided, recorded because they cost time

- `cargo test -p spt --lib <filter>` printed `error: no library targets found in
  package spt` and **exited 0**. A vacuous green wearing a pass's clothes. The
  spt crate is binary-only; the filter must run under `--bins`. Caught by
  opening GATE-TEST-INDEX before the run, which is the whole reason that rule
  exists.

## THE FIRING POPULATION, AS A CENSUS (doyle asked for this by name)

Counted `cfg(test)`-aware — the test-module boundary per file was computed
first, because a grep cannot see `#[cfg(test)]` and a mixed list still reads as
an enumeration.

**`AccessStore::apply_mutation` — the store seam. 10 PRODUCTION call sites:**

| site | what it is | reaches the notice? |
|---|---|---|
| cli.rs:15070 | inside `tuple_mutation` itself | the only one that can |
| cli.rs:17288, :17405 | `cmd_knock_approve` (both halves of the authority split) | no |
| cli.rs:18040 | `write_own_reverse` (the reverse grant a mutual knock earns) | no |
| cli.rs:18418, :18531 | `redeem_local` (invite-code redemption) | no |
| cli.rs:18956 | `cmd_access`, the POSITIONAL allow arm | no |
| spt-daemon/answerop.rs:113 | the knock-answer receipt path | no |
| spt-daemon/redeemop.rs:143, :227 | the wire-side redemption | no |

Everything in spt-store/access.rs (23 hits, all past the `mod tests` at 2097)
and in spt-store/knock.rs (3 hits, all past 1638) is inside a test module.

**`tuple_mutation` — 4 production call sites, and the predicate admits ONE:**

| site | spelling | admitted? | excluded by |
|---|---|---|---|
| cli.rs:18840 | `access allow` (precise) | **YES** | — |
| cli.rs:18861 | `access deny` | no | decision |
| cli.rs:18882 | `access remove` | no | op |
| cli.rs:18686 | `spt api access-*` / `--for`, the ER seat | no | `via_daemon_seat` |

So: 1 of 4 call sites of the one body of 10 that can reach it.

## PER-ARM MUTATION PROOF (predictions registered BEFORE the run)

Two arms mutated in ONE build:

- **M1** — dropped the `!via_daemon_seat` term from `inert_notice_population`.
- **M2** — disabled the empty-population guard in `inert_grant_with`, so an
  empty `Concrete` falls through the loop and reports `ChangesNothing`.

Registered predictions: population cell RED, named-absence cell RED, verdict
cell GREEN, int GREEN. Measured, exactly:

```
a_subject_with_no_origin_gets_a_named_absence_and_never_an_inert_verdict ... FAILED
the_inert_notice_speaks_only_for_the_operators_own_precise_allow ........... FAILED
the_inert_grant_verdict_follows_the_chain_not_the_endpoints_posture ........ ok
access_precise_allow_e2e (5 arms) .......................................... PASS
```

Restored from the committed baseline and re-greened.

**AND THE HONEST LIMIT OF THAT PROOF, stated so its green is not over-read:**
the int's arm 5 (the redemption path prints nothing) CANNOT be reddened by any
in-place mutation of this code, including M1 — because `redeem_local` never
reaches `tuple_mutation` at all. That exclusion is STRUCTURAL, not predicate-
borne. Arm 5 therefore guards against a future MOVE of the notice down to the
seam; the predicate's seat exclusion is guarded by the unit population cell,
and neither arm covers the other's failure.

## LANE SLATE (tip `6301a289`)

- clippy `--workspace --all-targets`: **exit 0, zero warnings** (one
  `redundant_closure` on my resolver, fixed in the tip commit)
- `cargo test -p spt --bins`: **645 passed, 0 failed** (88.78s)
- `cargo nextest run -p spt --test access_precise_allow_e2e`: **1/1 PASS**
- `traceable-reqs check`: **real exit 0**, new REQ `[OK] +impl +unit +int`
- `cargo run -p xtask -- check`: **OK**, exit 0

## TWO TRAPS HIT, both recorded because both produce a FALSE GREEN

1. `cargo test -p spt --lib <filter>` prints `error: no library targets found in
   package spt` and **exits 0**. The `spt` crate is binary-only; filters need
   `--bins`. Caught by opening GATE-TEST-INDEX before the run.
2. `traceable-reqs check | tail -8` reports **tail's** exit status, not the
   check's — and the tail also swallowed the line naming the new REQ, so the
   "exit 0" I first read was evidence about `tail` and nothing else. Re-run
   unpiped, `REAL_EXIT=0`, and the REQ's own line read back by name.

## What is owed

1. Full lane slate: `--bins` unit run, the int cell, clippy `--workspace
   --all-targets`, `traceable-reqs check`, `xtask check`.
2. ~~FINDING 5 (the false `ACCESS_LOCKED`)~~ MEASURED AND FILED as
   **releases#206**, with the transcript in the body. It is WORSE than the
   census predicted — three faces, not one: the `ACCESS_LOCKED` line is false;
   the JSON view (`"locked": true`) and the human view ("every decision comes
   from open fallbacks") contradict each other on the same store in the same
   breath; and a VACANT record survives `revoke` (`{"endpoint": "lockme"}`),
   which `is_vacant`'s own doc says must never read as restricted — and which,
   through `is_locked`, is exactly what it reads as. NOT PORTER scope, no fix in
   this lane.
3. Report to doyle with the firing-population census; board comment on
   releases#196.
4. Pool release when the lane is picked at assembly. DO NOT rebase or push.

## Do not touch

The wave-2 hold set: #202 `d5351e66` · #204 `f1bd2679` · #201 `e3e4524f` · #199
`fd84e160` · IR-53 `dc2143b0` · rider `e02f565f`. doyle's gate rig
`.worktrees/gate-d5351e66`.
