# W1 tranche-2 — #109 perch GC — JIT plan (todlando)

Written 2026-08-04, after #6 and #75 were handed to doyle's gate. NOTHING is
built for #109 yet and nothing should be until step 1 is answered and reported.

## Standing constraints (doyle, on the issue — binding, not advice)

1. **UNKNOWN fails toward NOT reaping.** A perch whose owning endpoint cannot be
   POSITIVELY resolved as purged is left alone. The predicate is positive —
   "this endpoint no longer exists in the authoritative store" — never an age or
   last-touched heuristic. A dormant endpoint, a suspended session and a
   long-idle live agent are all indistinguishable from "old" by mtime, and this
   box hosts other agents' live infrastructure.
2. **Name the predicate BEFORE building the sweep.** First deliverable is not
   code: what exactly makes a perch's endpoint purged, WHICH store answers that
   authoritatively, and what the answer is when that store is unavailable
   (which must be: do nothing).
3. **Negative control first.** Prove the sweep DECLINES to reap a live
   endpoint's perch, on purpose, before proving it reaps a dead one. A sweep
   that reaps everything and a sweep that reaps correctly produce the same
   result on a box that only holds junk.
4. **Report the MEASURED population on HFENDULEAM** — how many perches, how many
   resolve to a live endpoint. The fix's value and its blast radius are the same
   number.

## KNOWN-HAZARDS 3.4 — the refusal posture this GC must match

Read in full before writing the sweep (`REQ-HAZARD-RING-PERCH-ADOPTION`). It
rules for `ring`, whose direction is "never ADOPT". The GC's direction is
"delete", so 3.4 does not transfer verbatim — but its *reasoning* is exactly
what the GC's refusal posture must inherit:

- Probe the perch **DIRECTORY**, never the ready marker. Marker-down is the
  NORMAL steady state on hosted OMP endpoints (the extension owns the listener
  and publishes busy/idle through `api state`), so a marker-first GC would
  misjudge healthy endpoints routinely.
- A dir carrying a **record or a spool** is OCCUPIED.
- **Unreadable** record or unreadable dir counts as OCCUPIED, never as residue —
  deletion is irreversible, refusal is recoverable.
- A pre-existing **empty** dir is NOT provably residue either: a bringup
  mid-create owns an empty perch dir for a window, and deleting it is the same
  race in a different hat. (emphasys's amendment revoked the empty-dir
  self-heal for `ring`; the GC must not quietly reintroduce it.)
- Refusal is **loud and distinct**, never silent.

Open question the lane must answer rather than assume: 3.4 makes a dir with a
record OCCUPIED, which — read naively — would forbid the GC from reaping
anything at all. The GC's authorization has to come from somewhere `ring` never
had: a positive statement that the OWNING ENDPOINT is gone. Whether that
authority outranks an occupied-looking dir, and for which of the four states
above, is a doyle ruling to obtain, not a call to make in the lane.

## Recon done so far (source-grounded, this session)

- `spt_store::perch::owlery_dir()` is the perch root;
  `perch::resolve_perch_path(id, ParentHint::Infer)` maps an id to its dir.
- `crates/spt/src/cli.rs::purge_endpoint_core_with` is the ONE purge path
  (`REQ-ENDPOINT-PURGE`, `REQ-PICKER-PURGE-SHORTCUT` routes the picker through
  it). It gates on `spt_store::liveness::is_perch_alive(&perch)` (offline-only
  unless `--force`), self-guards on `SPT_ENDPOINT_ID`, and the engine room is
  reset rather than deleted.
- `spt_store::liveness::is_perch_alive` is the existing liveness predicate and
  is the first candidate for the "declines to reap a live endpoint" arm.

## Immediate next steps

1. **Answer step 1 in writing.** Read `spt_store::perch` (dir layout, what a
   perch dir legitimately contains) and whatever store enumerates endpoints
   authoritatively; determine what a purge LEAVES BEHIND that makes a dir
   outlive its endpoint at all — that residue shape IS the predicate's subject.
   State the unavailable-store answer explicitly as "do nothing".
2. **Measure HFENDULEAM.** Count perch dirs under the owlery; classify each as
   resolves-to-live / resolves-to-known-endpoint / resolves-to-nothing. Report
   the three numbers to doyle with the command that produced them. Read-only —
   no deletion, no mutation of any perch.
3. Put the predicate + the population to doyle and get the ruling on the
   occupied-dir-vs-purged-owner tension above BEFORE minting a REQ.
4. Only then: mint the REQ, build the sweep, negative control first.

## Lane hygiene

- #6 `build/w1t2-shell-relink-force` @ `80a36a7` and #75
  `build/w1t2-subnet-status` @ `3cf720c` are both HANDED TO DOYLE and queued at
  his gate. **Do not touch either branch or worktree** — a new commit changes
  the sha he is gating.
- #109 gets its OWN fresh lane off main when it is time to build, not before.
- Merge-queue hold is LIFTED (doyle, 2026-08-04: v0.54.0 tagged at `86f0d84`,
  main ff'd, pushes open). Gate verdicts still come from doyle.
