Refuse teardown when a different live task owns the worktree #23
Loading…
Reference in a new issue
No description provided.
Delete branch "fm/fm-teardown-worktree-ownership-guard"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What was broken, and what it cost
bin/fm-teardown.shguarded the finished task's unlanded work, but never checked whether a different, live task occupied the worktree named in that finished task's metadata. Treehouse worktrees are pooled and recycled, so that record goes stale the moment a slot is reissued.Proven live on 2026-08-29, destructively. Four merged Conduitmail tasks were torn down to quiet a noisy digest. Three of them still recorded pooled slots that
cond-auth-provider-moduleandcond-forms-redirect-contractwere at that moment using. Teardown killed one worker's endpoint outright and hard-reset both trees mid-run — including a commit that had just applied three captain rulings, with an active validation run against it.The teardown refusal is the only protection unlanded work has, and it inspected the wrong task entirely. It reported success while destroying running work. Two of the six candidates happened to refuse on "uncommitted changes present" — but that fired for the recycled slot's current contents, which is accidental protection, not the guard working.
Recovery was luck: the validation gate's own repository still held the branch.
What this changes
An ownership check runs before any destructive step. If a different live task owns the worktree, teardown refuses and names that task, so the operator can act rather than guess.
--forcealone does not bypass it. A separate--discard-live-owneris required, and must match. That separation is deliberate: this is a different hazard from "this task's own work is unlanded", and sharing an escape hatch would mean someone forcing past the familiar refusal and silently getting this one too.A stale slot — where the recorded worktree no longer belongs to that task at all — clears the task's state without touching the tree.
Tests
Six, colocated. The two that decide whether this guard is worth having:
Plus:
--forcealone refuses, a matching discard overrides, a mismatched discard refuses, and a stale slot clears state without touching the tree.Note
Since the incident, firstmate has been running this check by hand before every teardown — reading live panes, comparing recorded worktrees, refusing anything occupied. That manual procedure has been safe through six teardowns and is what this encodes. Cleanup has otherwise been suspended all day, which is why finished work has been re-announcing itself in every digest.
Pipeline passed with zero findings across review, test, document and lint. Pushed at
8d76eb93.