---
name: pool-claim-holder-death-is-not-lane-state
description: "A POOL-OWNER.json holder read as DEAD proves nothing about the lane — the holder is a session process that /clear or a restart ends; measured 3/3 dead holders including a LIVE lane. Decide lane state from git ancestry, not the claim."
metadata: 
  node_type: memory
  type: project
  originSessionId: 8cc3808a-4878-4e70-9632-2ed3668002bf
  modified: 2026-08-04T03:35:47.287Z
---

**Measured 2026-08-03 on hfenduleam, checking the pid+`holder_started_at` identity PAIR (not
a bare pid) on three pools: all three read holder-DEAD** — ir15 (pid 30944), my own
hertz-ci-riders (pid 31380), and **ir21-xtask (pid 54408), which doyle had just named a LIVE
lane**. The holder field records the claiming SESSION process, and a `/clear`, a wake, or a
restart ends it while the lane continues.

So the claim's design premise — "the claim names a long-lived holder with its birth stamp, so
a recycled pid cannot read as a live lane" — closed the false-ALIVE arm and left the
**false-DEAD arm wide open**. Holder liveness carries no lane signal in either direction.

**Why:** the reap decision that matters ("is this pool's lane finished?") is a question about
a BRANCH, and the claim answers a question about a PROCESS. doyle accepted this as a class
finding.

**BUILT 2026-08-03**, branch `ci/poolowner-lane-claim` (8ce40d9 + 98cfafe + b75258d off
b7b00c3, unpushed, own thin lane in the riders' batch): claim carries `lane_branch` +
`lane_base`, ancestry decides, holder advisory. **Two properties that outlive the lane:** (a)
the ENFORCER is the build script of the ARRIVING tree, never the claim — so an old tree still
takes a live lane's pool and coverage grows only as trees turn over; doyle REFUSED a
stamp-version line for this, because the tree that must refuse is the one that does not read
the new field. (b) `pool-release` clears such a claim but is NOT the exit — the pool then
refuses on the unclaimed-foreign arm; the exit is a hatch-prefixed `pool-claim`.

**⭐⭐ 2026-08-03, LATER THE SAME DAY — THE FALSE-LIVE ARM, CAUGHT IN THE FIELD, AND THE MIRROR
PAIR THAT SETTLES THIS.** During the 44-worktree reap I read two claims an hour apart that fail in
OPPOSITE directions on the same box:
- **FALSE-LIVE (new, and the first of this polarity).** `hertz-ci-riders\target` named `holder_pid
  31380` with `holder_started_at 134302739211259713` = 16:38:41. Pid 31380 at read time was
  `claude.exe -n "doyle @ HFENDULEAM (spt-core/)"`, created **17:31:11**, FILETIME
  134302770715019370. Same boot, ~52 min apart, **stamps MISMATCH — the OS reissued a dead
  claimant's pid to the gater's own session.** A bare-pid probe reads that claim as held by a
  running holder and refuses, *naming doyle as the holder of hertz's pool*. The
  `holder_started_at` arm discriminates it correctly — this is that field caught DEFENDING, not
  asserted to defend ([[stable-anchor-is-not-a-recycling-defense]]).
- **FALSE-DEAD, simultaneously.** `ir24-reap\target` named `holder_pid 33612`, dead — while that
  lane was genuinely live, with `cargo clippy -q -p spt-daemon --lib --tests` (pid 22452) running
  under todlando's session as I read it.

**So the record is no longer "3/3 dead holders, one of them a live lane" — it is that holder
liveness misreads in BOTH directions on ONE box inside ONE hour.** It has no sound polarity and
cannot be rescued by picking a longer-lived holder. ⚠ And the recycle is same-BOOT: this and
[[pid-reuse-across-reboot]]'s 30s case mean a reboot is not required and "has the box rebooted?"
is not a screening question.

**How to apply:** to decide whether a pool is reclaimable, ask git — `git -C <worktree>
rev-parse HEAD` then `git merge-base --is-ancestor <head> main` plus a clean `status`. That is
what authorised the 2026-08-03 reap of ir15 (47.99 GiB) and ir18 (12.30 GiB): merged lanes,
real dirs (no reparse point), no inbound junctions aimed at them, no process running from
them, worktrees and branches left intact. Sibling defect, same file:
[[worktree-pool-claim-false-orphan]] (the false-ORPHAN arm). See also
[[pid-reuse-across-reboot]], [[stable-anchor-is-not-a-recycling-defense]],
[[worktree-pin-holder-triage]].

**⭐⭐ A DISPLACED CLAIMANT IS BLIND — measured 2026-08-03, 3 arms + competence control (doyle's
follow-up 2, "measure whether the loss is observable BEFORE designing a detector").** Rig: A claims
pool P, B claims P (takeover), read P; negative control B claims fresh Q that A never touched;
competence control A claims R untaken. Results: **P-after-takeover and Q-never-claimed are
BYTE-IDENTICAL** (modulo pool path); **zero residue of A in P**; R≠Q so the rig can see a difference
when one exists. **The takeover is silent on BOTH sides** — B's claim printed the ordinary success
line, no mention of a displaced owner, so B cannot warn A even in principle.
**Mechanism:** `pool_claim` never calls `read_owner` — it builds a `PoolOwner` from its own cwd and
`write_owner`s it as a plain `fs::write`. No prior-owner field, no read-before-write. The stamp is a
single mutable cell with room for exactly one owner and no history.
**⭐ FIELD INSTANCE OF (a), CAUGHT PROSPECTIVELY — 2026-08-03, todlando, IR-12 lane.** The
turnover property above is not theoretical and it bites the WRITER, not only the enforcer. At
lane start I claimed a fresh pool with `<root>\target\debug\xtask.exe` — the MAIN checkout's
prebuilt binary, on a checkout whose HEAD was 11169c1. It printed `(holder pid 34920, born …)`
and wrote a FLAT claim: `owner_tree`, `lane_label`, `holder_pid`, `holder_started_at`,
`written_by`. Main's own source at that same sha (`xtask/src/main.rs:2282`) prints
`(branch …, base …; advisory holder pid …)` and writes a NESTED `lane` carrying `branch` +
`base`. **A lane claimed with a stale xtask therefore records NO git identity — leaving the
pool judgeable only by holder pid, the exact predicate this whole file exists to refuse.**
Rebuilding xtask from the lane's own worktree and re-claiming wrote `lane_base` correctly.
**Detection method, which is the transferable part:** I read the binary's PRINTED claim line
against the source I had just read, and the shapes disagreed — a prebuilt tool is a claim about
a TREE just like a source read is ([[source-read-needs-its-sha-and-ancestry]]), and its mtime is
not its provenance. **How to apply:** build the claiming tool from the lane's own tree before
claiming, or diff the printed line against source; never assume `target/debug/*.exe` matches the
checkout it sits in. doyle filed this as a field instance of IR-26's bootstrap property rather
than a new entry. Bless-var sibling from the same lane: [[bless-env-turns-a-drift-gate-into-a-write]].

**Consequence for design:** "how do we notify the claimant" is the WRONG question — there is no
event to hang it on, since no actor observes the takeover as it happens. A's only channel is its
next build refusing (`build.rs` → `decide()` → owner_tree=B), which reports a STATE, never a
transition, and says exactly what it would say if the pool had always been B's. **The guard
adjudicates crossings at BUILD time and is entirely absent at CLAIM time — `pool-claim` is a hole
straight through the protection.** ⚠ Measured with `<root>\target\debug\xtask.exe` (mtime 08-03
08:19, competence-read as the ORIGIN/MAIN guard — its output carries no branch/base); that the
answer carries to the lane tip b75258d is a SOURCE read, not a measurement.
