Skip to content

fix(coord): count a session in a deregistered checkout as unplaceable - #1243

Merged
wshallwshall merged 3 commits into
mainfrom
guard-occupancy
Sep 17, 2026
Merged

wshallwshall merged 3 commits into
mainfrom
guard-occupancy

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

The occupancy fence could not refuse on the one ground that matters. A session record whose cwd lies
inside this repo family but matches no registered worktree was dropped with a bare continue and
never reached $faults, so RecordsUnplaceable could not rise and Available could not go false
because of it.

That is the manufactured-evidence shape. Available: true was unfalsifiable on that ground, so it
proved nothing, and two proposed reapers are gated on it.

The control fired first

Against the unpatched script at b8421ca53, with only the new test added:

2 failed, 2 passed in 7.85s
exit: 1

AssertionError: a record naming a checkout of this repo that git no longer lists reached no counter
{'RepoFound': True, 'Available': True, 'RecordsExamined': 1, 'RecordsUnplaceable': 0, 'Sessions': []}
assert 0 >= 1

That receipt is the defect stated in its own output: the record existed, parsed, and reached no
counter. Patched:

4 passed in 5.08s
exit: 0

Neighbours, same venv, unpiped, exit echoed separately: 98 passed in 151.06s across
test_coord_presence, test_coord_claim_liveness, test_coord_box_activity, test_coord_recoverable,
test_coord_overlap_attribution, test_coord_handoff_report, test_session_context_presence and
test_worktree_remove. test_worktree_prune_merged.py: 79 passed in 247.29s. ruff format --check,
ruff check and mypy clean. HEAD was b8421ca53 before and after every run, so no run measured a
moving tree.

The boundary, and why it is drawn there

A cwd OUTSIDE the repo family is not a fault -- it is another repository's session. The fault is a cwd
INSIDE the family that matches no registered worktree.

cwd state Verdict Evidence
Inside a registered worktree placed never reaches the new code
Exists, its .git pointer resolves to THIS repo's common dir FAULT positive proof, and it survives deregistration
Exists, resolves elsewhere or nowhere not a fault another repo, or a plain folder
Gone, named <primary>-<slug> FAULT nothing on disk can say whose it was; the naming new.ps1 builds is the only evidence left
Gone, not named that way not a fault

The name test is admitted ONLY when the directory is absent. A name match on a directory that exists
is the sibling-prefix trap that test_coord_presence.py::test_sibling_prefix_directory_is_not_treated_as_inside_the_repo
already pins, and MessageFoundry-vault sits beside the primary on this machine. Faulting it would
leave the fence permanently unavailable, which disarms every caller as thoroughly as never refusing.

Get-OwningGitCommonDir walks the filesystem rather than asking git, deliberately: git -C <path> rev-parse fails outright once a worktree's admin entry is pruned, which is precisely the state being
asked about.

Callers

All four already fail closed, so this can only move them toward refusal.

Caller Change
prune-merged.ps1 one more route to EXIT_REFUSED; every candidate becomes SKIP. The fence is re-read once before the apply loop, not per candidate.
presence.ps1 stdout stays []; the UNAVAILABLE receipt goes to stderr. JSON contract untouched.
claim.ps1 Get-OccupancyOnce returns false, so a holder stays present, which already refuses.
remove.ps1, box-activity.ps1 read occupant lists, not Available. Unaffected.

No field or signature was removed.

Live reading, patched

6 config roots, 7 records, 0 unplaceable, 110 registered worktrees, 3 sessions placed, Available: True.
The new fault does not disable today's fence. That zero is reportable because the planted controls in
the test fire.

Two corrections to the brief behind this commit

The brief asked for a plant pointing at a deleted .claude/worktrees path. Those sit under a
REGISTERED worktree, so such a record always matches an ancestor and is placed. That plant would have
gone green against the unpatched script and proved nothing. The path has to be under a checkout git no
longer lists.

The brief also said Available could never go false. Too strong: three paths already set it false --
no config root, an unreadable registry, and zero readable records. All three mean "I could not look at
all". What could not happen was Available going false because a session could not be PLACED, which is
the only one that refuses on positive evidence.

Known follow-up, deliberately not in this branch

scripts/worktree/prune-merged.ps1's FENCE UNAVAILABLE header paragraph is an incomplete enumeration.
It omitted the cwd-less shape before this change and omits two of three after it. A separate session is
holding a patch for it, derived from Get-WorktreeOccupancy rather than from any prior comment, and is
waiting on this branch to land.

occupancy.ps1 dropped a record whose cwd matched no registered worktree on a
bare `continue`, so it reached no counter. RecordsUnplaceable could not rise
past the two shapes the matcher already counted, Available could not go false
for a third, and a session left behind by a worktree git had stopped listing
was INVISIBLE rather than UNPLACEABLE.

That is a fence that cannot fail, so its green light measured nothing -- and
prune-merged.ps1 and claim.ps1 both gate on it.

The boundary is the whole of the change and it cuts both ways. Another
repository's session must not cost a refusal, because most records on this host
are exactly that, so a fault needs evidence the cwd is a checkout of THIS repo:

  * the directory is still there -- read its own .git pointer and compare git
    directories. That survives deregistration where `git rev-parse` does not,
    and a plain `<primary>-*` lookalike fails it;
  * the directory is gone -- nothing on disk can say whose it was, so the
    `<primary>-<slug>` naming new.ps1 builds is the only evidence left. The name
    test is admitted on this branch only, never on a path that exists.

Placement moves ahead of the availability verdict: while the verdict was
computed first, a failed placement had nowhere to go.

tests/test_coord_occupancy_unplaceable.py asserts BOTH directions, because a
test that only proves refusal passes against a fence wired shut. Against the
unpatched script the two fault tests FAIL and the two availability tests pass;
patched, all four pass.

Measured on this host with the patch applied: 6 config roots, 7 records, 0
unplaceable, 110 worktrees, 3 sessions placed -- so the new fault does not
disable the live fence.
@github-actions github-actions Bot added the ci-red A required check went red. Attribute it before retrying. label Sep 17, 2026
wshallwshall and others added 2 commits September 17, 2026 14:58
tests/test_coord_occupancy_unplaceable.py imports no engine module, so
nothing decided which CI legs run it and test_tooling_partition.py failed
the required ubuntu leg. That test's own message requires the entry to land
in the same pull request as the file.

Classified (a) HARNESS rather than the ambiguous (b): the subject is
scripts/coord/occupancy.ps1, and the manifest header names the coordination
harness as this tier. Twenty-six sibling test_coord_*.py files already sit
here.

Placed among those siblings rather than appended at end-of-file. Nothing
enforces an order, only test_manifest_has_no_duplicates, and an EOF append
is the one position that collides with every other pull request adding a
line.

Added by the Lander, not the authoring session, because the branch had been
quiet for four hours with no claim on it and a third session is blocked
until this commit is reachable from main. The head SHA was pinned before
the write and the push refuses if it moved.
…S.md

occupancy.ps1:30 changed from "Two shapes qualify" to "Three" on this
branch; docs/WORKTREES.md still enumerated two, so a documented claim
stopped matching its code. No test asserts it either way.

The enumeration, not just the numeral. The third shape is a record whose
cwd is a checkout of this repo that `git worktree list` no longer
carries. Taken from Get-UnplaceableCwdReason, not the script header:
that function admits two evidence branches -- the owning git common dir
when the path exists, the `<primary>-` name when it does not -- and the
header covers only the first. Hence "still on disk or gone".

Agreement words that moved with the count: "and one that parses" -> "a
record that parses", "both used to be" -> "Each used to be", "Neither
can be placed" -> "None can be placed". The "two independent signals"
and "Both are re-read" above count signals, not shapes, and stand.

An older copy in prune-merged.ps1:84 names only the unparseable shape.
Left untouched -- another session owns that header.
@wshallwshall
wshallwshall added this pull request to the merge queue Sep 17, 2026
Merged via the queue into main with commit 1966f75 Sep 17, 2026
41 checks passed
@wshallwshall
wshallwshall deleted the guard-occupancy branch September 17, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-red A required check went red. Attribute it before retrying.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant