A fresh polecat spawn reuses a worktree that is still another bead's refinery-held work_dir, so the formula's branch-setup would reset the checkout under the running merge gate #19

Open
opened 2026-09-06 16:51:50 +00:00 by forgejo-admin · 1 comment

Measured 2026-09-06 ~16:40Z, polecat gastown.furiosa spawning onto bead ce-21hp.

The supervisor spawned a fresh furiosa session into /Users/pbanka/gc-scratch/.gc/worktrees/celilo/polecats/gastown.furiosa for the new claim. That directory was still recorded as metadata.work_dir on bead ce-kg5o (polecat/ce-kg5o, branch polecat/ce-kg5o at 1088a522), which was already handed off: status open, assignee celilo/gastown.refinery, and the refinery was ACTIVELY running its merge gate in that directory at the same moment (e2e results for the alerting suite written at 15:48Z, 16:07Z and 16:25Z).

The new session's formula (mol-polecat-work, branch-setup step) resets the checkout to polecat/<new-bead> from origin/main and records that on the new bead. Executed blindly in that directory it would have:

  1. Destroyed the refinery's checkout mid-gate — the branch ce-kg5o's metadata.branch points at would no longer exist locally, breaking crash-recovery and the handoff contract.
  2. Raceted with the refinery's own git operations in the same gitdir.

Nothing in the spawn path checks whether the directory it is about to hand a fresh session is still claimed by another bead's work_dir metadata. The session name (polecats/gastown.furiosa) is reused across beads, so every respawn of a pool session re-enters the last occupant's directory.

Caught by accident: the polecat ran git branch --show-current before any setup and found polecat/ce-kg5o. Expected: either the spawn path refuses/redirects when the target worktree's checked-out branch or any bead's work_dir metadata still claims it, or the refinery handoff clears/relocates work_dir when the bead leaves the polecat.

Worked around by hand: created a nested worktree polecats/gastown.furiosa/worktrees/ce-21hp on branch polecat/ce-21hp and recorded THAT on ce-21hp's metadata, leaving the parent checkout untouched. Same pattern as the existing polecats/gastown.furiosa/worktrees/ce-rctr.

Related: gascity#1 (stale rebase debris in a reused worktree), gascity#7 (worktree provenance keys), gascity#18 (drain-after-claim race). This is the allocation half none of them cover: reuse while the previous owner is still live.

Measured 2026-09-06 ~16:40Z, polecat gastown.furiosa spawning onto bead ce-21hp. The supervisor spawned a fresh furiosa session into `/Users/pbanka/gc-scratch/.gc/worktrees/celilo/polecats/gastown.furiosa` for the new claim. That directory was still recorded as `metadata.work_dir` on bead **ce-kg5o** (`polecat/ce-kg5o`, branch `polecat/ce-kg5o` at 1088a522), which was already handed off: status open, assignee `celilo/gastown.refinery`, and the refinery was ACTIVELY running its merge gate in that directory at the same moment (e2e results for the alerting suite written at 15:48Z, 16:07Z and 16:25Z). The new session's formula (mol-polecat-work, branch-setup step) resets the checkout to `polecat/<new-bead>` from origin/main and records that on the new bead. Executed blindly in that directory it would have: 1. Destroyed the refinery's checkout mid-gate — the branch ce-kg5o's `metadata.branch` points at would no longer exist locally, breaking crash-recovery and the handoff contract. 2. Raceted with the refinery's own git operations in the same gitdir. Nothing in the spawn path checks whether the directory it is about to hand a fresh session is still claimed by another bead's `work_dir` metadata. The session name (polecats/gastown.furiosa) is reused across beads, so every respawn of a pool session re-enters the last occupant's directory. Caught by accident: the polecat ran `git branch --show-current` before any setup and found `polecat/ce-kg5o`. Expected: either the spawn path refuses/redirects when the target worktree's checked-out branch or any bead's `work_dir` metadata still claims it, or the refinery handoff clears/relocates `work_dir` when the bead leaves the polecat. Worked around by hand: created a nested worktree `polecats/gastown.furiosa/worktrees/ce-21hp` on branch `polecat/ce-21hp` and recorded THAT on ce-21hp's metadata, leaving the parent checkout untouched. Same pattern as the existing `polecats/gastown.furiosa/worktrees/ce-rctr`. Related: gascity#1 (stale rebase debris in a reused worktree), gascity#7 (worktree provenance keys), gascity#18 (drain-after-claim race). This is the allocation half none of them cover: reuse while the previous owner is still live.
Author
Owner

Partial fix preserved, and a ruling. A celilo polecat (nux) attempted this on 2026-09-06 and was drained mid-work; the mayor preserved the edits as a WIP commit on polecat/ce-7346 in ~/dev/gascity/worktrees/ce-7346 (dedaf91fc, local only).

What it has: one batched work-dir claim scan per desired-state build (cmd/gc/workdir_claim_scan.go), memoized, mapping normalized work-dir paths to non-terminal beads, distinguishing session beads from work beads; the reaper routed through it; and a spawn veto on the fresh-create and rebind paths.

What it lacks: the veto only refuses (returns the #7 evidence error, so the request is skipped and the pool stalls behind the refinery while the slot's directory is claimed); there is no redirect to a fresh directory; a veto on the live-rebind path would drop the session from the desired set, which is the #18 drain; and no test proves the guard fails first.

peba, 2026-09-06 11:12 PDT: keep the scan helper. Whoever finishes this builds on workdir_claim_scan.go, adds the redirect (allocate a fresh directory, the nested-worktree shape the workaround used), and a failing test for the reuse case. #18 stays a separate change.

**Partial fix preserved, and a ruling.** A celilo polecat (nux) attempted this on 2026-09-06 and was drained mid-work; the mayor preserved the edits as a WIP commit on `polecat/ce-7346` in `~/dev/gascity/worktrees/ce-7346` (dedaf91fc, local only). What it has: one batched work-dir claim scan per desired-state build (`cmd/gc/workdir_claim_scan.go`), memoized, mapping normalized work-dir paths to non-terminal beads, distinguishing session beads from work beads; the reaper routed through it; and a spawn veto on the fresh-create and rebind paths. What it lacks: the veto only refuses (returns the #7 evidence error, so the request is skipped and the pool stalls behind the refinery while the slot's directory is claimed); there is no redirect to a fresh directory; a veto on the live-rebind path would drop the session from the desired set, which is the #18 drain; and no test proves the guard fails first. **peba, 2026-09-06 11:12 PDT: keep the scan helper.** Whoever finishes this builds on `workdir_claim_scan.go`, adds the redirect (allocate a fresh directory, the nested-worktree shape the workaround used), and a failing test for the reuse case. #18 stays a separate change.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
celilo/gascity#19
No description provided.