What happened?
From a scruff lane I ran scruff child <workshop path> to do some work in
another repo. It created the lane and printed the path, and I cd'd into it —
git status and git branch --show-current both answered normally, so the
checkout was really there.
A minute or so later, before I had written anything into it, the checkout and
its branch were both gone. git worktree list in the workshop didn't have it
and git branch --list 'worktree-acceptance*' was empty.
scruff reaped says what took it:
2026-09-17T08:33:48Z hausfold-workshop/acceptance-suite-timed — reaped: never-diverged
recover: git -C <hausfold-workshop checkout> branch worktree-acceptance-suite-timed a460bc29b4db
never-diverged, and at the same second as another lane's reap of an unrelated
repo — so a scruff reap running in one of the other panes on this machine
swept it.
That reads correct to the letter: a child lane that has just been created has no
commits, so its branch is exactly at main, and ancestry says landed. It is
just that "landed" is the wrong word for a lane nobody has had a chance to
commit into yet.
Expected: a lane created seconds ago survives a concurrent scruff reap.
Got: it was swept before anything was written into it. I worked around it by
re-creating the lane and immediately making a throwaway anchor commit so its
branch would no longer be at main.
Nothing was lost — invariant 1 held, because there was no work in it yet — so
this is a surprise rather than a data-loss bug. But it is a surprise in the one
direction that matters: I had a path in hand from a command that had just
succeeded, and the ground went out from under it.
Where
scruff reap and scruff child, together. I don't think either is wrong on its
own.
The occupancy gate that would normally save a lane doesn't apply here, and
structurally can't: a child lane's pane is the parent's pane, in a different
repo's checkout — scruff child is the documented way to work on another repo
from the pane you are already in, so there is never anything standing in the
child checkout itself. That leaves ancestry as the only gate, and ancestry says
landed for every brand-new lane.
Two shapes that would fix it, and the second seems more in keeping:
- a grace window on the registry row's creation time — don't reap a lane younger
than N minutes, whatever ancestry says;
- treat
never-diverged as not landed when the lane has no commits of its
own and has a registered parent. A lane that has never diverged has not
landed; it has not started. Those are different states and only one of them is
reapable. reap --dead-ends already draws a distinction like this for lanes
nothing will ever land.
Diagnostics
scruff 1.5.0 · schema 2 · darwin/arm64
base
path ~/.cache/scruff
resolved from the default (~/.cache/scruff)
registry ~/.cache/scruff/registry.tsv — 2 rows
state ~/.local/state/scruff
environment
git 2.54.0 · /usr/bin/git
forge gh 2.99.0 — authenticated
occupancy lsof vouches for absence — `scruff reap` can sweep live checkouts
leases 0 held · ~/.local/state/scruff/live
reflink supported — `cp -c` clones on ~/.cache/scruff
config ~/.config/scruff/config.toml · agent=claude · namer=api · hooks: focus, open, resume
lanes 2 across 2 repos — 2 live, 0 parked, 0 stray
findings
none — nothing here needs a human
(Home directory rewritten to ~; nothing else changed.)
Anything else
Several agent panes were running against different repos at the time, which is
what made it likely — this needs two lanes to hit, one creating a child and one
reaping, and the window is however long it takes you to start working.
The same scruff reaped line shape shows it is recoverable after the fact,
which is the part that worked exactly as advertised.
What happened?
From a
scrufflane I ranscruff child <workshop path>to do some work inanother repo. It created the lane and printed the path, and I
cd'd into it —git statusandgit branch --show-currentboth answered normally, so thecheckout was really there.
A minute or so later, before I had written anything into it, the checkout and
its branch were both gone.
git worktree listin the workshop didn't have itand
git branch --list 'worktree-acceptance*'was empty.scruff reapedsays what took it:never-diverged, and at the same second as another lane's reap of an unrelatedrepo — so a
scruff reaprunning in one of the other panes on this machineswept it.
That reads correct to the letter: a child lane that has just been created has no
commits, so its branch is exactly at
main, and ancestry says landed. It isjust that "landed" is the wrong word for a lane nobody has had a chance to
commit into yet.
Expected: a lane created seconds ago survives a concurrent
scruff reap.Got: it was swept before anything was written into it. I worked around it by
re-creating the lane and immediately making a throwaway anchor commit so its
branch would no longer be at
main.Nothing was lost — invariant 1 held, because there was no work in it yet — so
this is a surprise rather than a data-loss bug. But it is a surprise in the one
direction that matters: I had a path in hand from a command that had just
succeeded, and the ground went out from under it.
Where
scruff reapandscruff child, together. I don't think either is wrong on itsown.
The occupancy gate that would normally save a lane doesn't apply here, and
structurally can't: a child lane's pane is the parent's pane, in a different
repo's checkout —
scruff childis the documented way to work on another repofrom the pane you are already in, so there is never anything standing in the
child checkout itself. That leaves ancestry as the only gate, and ancestry says
landed for every brand-new lane.
Two shapes that would fix it, and the second seems more in keeping:
than N minutes, whatever ancestry says;
never-divergedas not landed when the lane has no commits of itsown and has a registered parent. A lane that has never diverged has not
landed; it has not started. Those are different states and only one of them is
reapable.
reap --dead-endsalready draws a distinction like this for lanesnothing will ever land.
Diagnostics
(Home directory rewritten to
~; nothing else changed.)Anything else
Several agent panes were running against different repos at the time, which is
what made it likely — this needs two lanes to hit, one creating a child and one
reaping, and the window is however long it takes you to start working.
The same
scruff reapedline shape shows it is recoverable after the fact,which is the part that worked exactly as advertised.