Skip to content

A brand-new scruff child lane is reaped before you can commit into it: no commits reads as never-diverged #137

Description

@JulienMartel

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageNot looked at yet. Every bug and idea starts here; a maintainer takes it off.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions