You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Widen the clone unpushed probe to every local ref #471
Make the clone-level unpushed probe see every local ref, not one branch. This is a live data-loss fix in shipped code and it is independent of everything else on this map.
Today dl's unpushed answer is log --oneline <branch> --not --remotes everywhere. So commits on any other local branch in the clone are invisible. Commit on wip, switch to main, let dl <ws> rm delete the clone, and those commits are gone — with NothingToLose printed. Measured on #459 and again on #446.
Sliced out of #454 deliberately, on two grounds. It lives in a different module from the agent-worktree sweep that #454 rewrites, so there is no conflict. And it fixes a path a user can lose work on today, which under this map's first principle should not wait behind a large rewrite.
Scope:
--all in place of the single named branch at clone scope, so the probe reaches every local branch. What proves an agent worktree safe to remove, and what if it cannot be proved #446 measured that rev-list --all --not --remotes in a clone reaches every local branch, refs/stash, and every worktree's HEAD including detached ones.
Readers to check, because they all consume this answer: dl <ws> rm's refusal, --prune's orphan arm, and the --ls --json goldens that pin unsaved.
Red first, and the failing test is the point: a clone with commits on a non-checked-out local branch, asserting the guard reports them rather than NothingToLose. That test is the bug report.
Two things explicitly not here, so the boundary is clean. The nested-agent-worktree half of the same blindness (.claude/worktrees/ is gitignored, so status --porcelain sees nothing) belongs to #454, because it needs the site fold that ticket builds. And the verdict type itself (Collectable(Proof) | Stands(NonEmpty<Reason>)) is #454's — this ticket keeps the existing shape and only widens the ref set it asks about.
One thing worth stating in the code: refs/stash is shared, written to <clone>/.git/refs/stash even from inside a linked worktree, and reached by --all. Measured on #446. So widening to --all picks up stashes as a side effect, which is correct and should be deliberate rather than incidental.
breadcrumb: claimed. Serial execution begins here at the human's instruction, with merge-when-green explicitly granted: implement, review, fix, merge, one ticket at a time. This ticket goes first because it is the smallest of the live data-loss paths and shares no module with the rest.
breadcrumb: building on wayfinder/devlaunch-471 off a fresh origin/main. Three seams, all pre-agreed by the ticket's "readers to check", and all against real git rather than a script, because the two bugs this probe has already had were invisible to anything else:
What a clone holds — the unsaved answer for a clone with commits on a branch that is not checked out. This is the red test and the bug report: today it reads NothingToLose.
What was asked — the argv the unpushed probe spells, pinned in the git client's scripted suite. The existing pin is there because the argument order is load-bearing; widening the ref set has to move that pin deliberately rather than silently.
The rm guard — the refusal over a real cache and real git, so the widening is proved to reach the reader that destroys things and not just the function that answers.
The --prune orphan arm and the --ls --jsonunsaved goldens consume the same answer through the same call, so they are re-run rather than re-seamed.
breadcrumb: built, PR #474. One slice: the clone probe asks --all instead of a branch name, and the branch parameter goes with it, because there is no longer a single branch the question is about. The refusal arm stopped carrying one too, for the same reason.
Three things worth carrying forward, none of them in the ticket.
A second data-loss path fell out of the same change, and it was the gate rather than the branch name. The probe was skipped entirely whenever HEAD named no commit. A clone left on an orphan branch therefore reported nothing to lose while holding unpushed commits on the branch it had just stepped off. Measured: --all exits 0 with no output on a clone with no refs at all, so the gate was protecting nothing and hiding that. Both paths are now pinned.
Widening to every ref means widening to refs/tags, and that is a false positive worth knowing about.--not --remotes excludes only refs/remotes, so a tag the remote itself carries reads as unpushed once no remote branch reaches it any more. Measured: tag a release on a branch, delete the branch upstream, and the clone reports one unpushed commit forever. Principle 1 settles it: a clone kept costs disk, a clone deleted costs the work, so the answer fails in the keeping direction on purpose. Narrowing the ref set to tidy the report would be principle 2 beating principle 1. Named in the probe's doc comment and in the cleanup page so it does not read as a bug to the next person.
The --prune reader is where this cost the most, and it is the assertion to keep. Proved red by putting the branch-scoped question back: the orphan clone holding the only copy of an hour of work was on the removal list, by name. rm refuses one clone at a time; prune was removing them by the dozen on the same blind answer.
Left alone deliberately: the gitignored .claude/worktrees/ half, and the verdict type. Both are #454's, and the existing Unsaved shape carried this without change.
breadcrumb: one CI-only correction, worth recording because the next ticket on this map will hit it too. CouldNotTell is on the binary surface, so dropping a field from one of its arms moves a row in the checked-in public-api snapshot, and the public-api job fails until that row is committed. The container carries the pinned cargo-public-api but no rustup, which its rustdoc-JSON backend needs, so the script that regenerates those files genuinely is a host command as its header says. A removed struct-variant field is one row of a sorted generated file, so it was removed by hand and CI's regenerate-and-diff is what proves it right. Nothing about the behaviour changed.
breadcrumb:#474 is green at efeef38203cdfade14c5d4aa562627b35cff3f3f, MERGEABLE, checks verified against the full SHA rather than the PR view. All 15 settled: ci, rust, e2e, gate, bench (via gate), packaging, prek, public-api, rust-coverage, review, GitGuardian and four codecov gates success; publish-job skipped, which is what it does off a release commit. Stopping here for review rather than merging.
breadcrumb: gate passed. PR #474, head efeef38203cdfade14c5d4aa562627b35cff3f3f, 15 checks green and MERGEABLE, so the merge ref existed and ci/prek really fired rather than silently not running. Handing to a fresh reviewer who did not write it.
breadcrumb: review posted on #474 — approve, no blocking findings on either axis. Claims (a) tag false positive as a deliberate P1 trade, (b) the dropped branch field, and (c) the unborn-HEAD gate closing a second data-loss path all re-measured against real git 2.51.1 and upheld. Seven non-blocking items; one worth a follow-up ticket: the tag trade is a P1 decision that no test holds, and the widening also reaches refs/notes and refs/bisect, which the doc comment does not name.
Activity
blooop commentedon Aug 25, 2026
breadcrumb: claimed. Serial execution begins here at the human's instruction, with merge-when-green explicitly granted: implement, review, fix, merge, one ticket at a time. This ticket goes first because it is the smallest of the live data-loss paths and shares no module with the rest.
blooop commentedon Aug 25, 2026
breadcrumb: building on
wayfinder/devlaunch-471off a freshorigin/main. Three seams, all pre-agreed by the ticket's "readers to check", and all against real git rather than a script, because the two bugs this probe has already had were invisible to anything else:NothingToLose.rmguard — the refusal over a real cache and real git, so the widening is proved to reach the reader that destroys things and not just the function that answers.The
--pruneorphan arm and the--ls --jsonunsavedgoldens consume the same answer through the same call, so they are re-run rather than re-seamed.blooop commentedon Aug 25, 2026
breadcrumb: built, PR #474. One slice: the clone probe asks
--allinstead of a branch name, and the branch parameter goes with it, because there is no longer a single branch the question is about. The refusal arm stopped carrying one too, for the same reason.Three things worth carrying forward, none of them in the ticket.
A second data-loss path fell out of the same change, and it was the gate rather than the branch name. The probe was skipped entirely whenever HEAD named no commit. A clone left on an orphan branch therefore reported nothing to lose while holding unpushed commits on the branch it had just stepped off. Measured:
--allexits 0 with no output on a clone with no refs at all, so the gate was protecting nothing and hiding that. Both paths are now pinned.Widening to every ref means widening to
refs/tags, and that is a false positive worth knowing about.--not --remotesexcludes onlyrefs/remotes, so a tag the remote itself carries reads as unpushed once no remote branch reaches it any more. Measured: tag a release on a branch, delete the branch upstream, and the clone reports one unpushed commit forever. Principle 1 settles it: a clone kept costs disk, a clone deleted costs the work, so the answer fails in the keeping direction on purpose. Narrowing the ref set to tidy the report would be principle 2 beating principle 1. Named in the probe's doc comment and in the cleanup page so it does not read as a bug to the next person.The
--prunereader is where this cost the most, and it is the assertion to keep. Proved red by putting the branch-scoped question back: the orphan clone holding the only copy of an hour of work was on the removal list, by name.rmrefuses one clone at a time; prune was removing them by the dozen on the same blind answer.Left alone deliberately: the gitignored
.claude/worktrees/half, and the verdict type. Both are #454's, and the existingUnsavedshape carried this without change.blooop commentedon Aug 25, 2026
breadcrumb: one CI-only correction, worth recording because the next ticket on this map will hit it too.
CouldNotTellis on the binary surface, so dropping a field from one of its arms moves a row in the checked-in public-api snapshot, and thepublic-apijob fails until that row is committed. The container carries the pinnedcargo-public-apibut norustup, which its rustdoc-JSON backend needs, so the script that regenerates those files genuinely is a host command as its header says. A removed struct-variant field is one row of a sorted generated file, so it was removed by hand and CI's regenerate-and-diff is what proves it right. Nothing about the behaviour changed.blooop commentedon Aug 25, 2026
breadcrumb: #474 is green at
efeef38203cdfade14c5d4aa562627b35cff3f3f,MERGEABLE, checks verified against the full SHA rather than the PR view. All 15 settled:ci,rust,e2e,gate,bench(viagate),packaging,prek,public-api,rust-coverage,review, GitGuardian and four codecov gates success;publish-jobskipped, which is what it does off a release commit. Stopping here for review rather than merging.blooop commentedon Aug 25, 2026
breadcrumb: gate passed. PR #474, head
efeef38203cdfade14c5d4aa562627b35cff3f3f, 15 checks green and MERGEABLE, so the merge ref existed andci/prekreally fired rather than silently not running. Handing to a fresh reviewer who did not write it.blooop commentedon Aug 25, 2026
breadcrumb: review posted on #474 — approve, no blocking findings on either axis. Claims (a) tag false positive as a deliberate P1 trade, (b) the dropped
branchfield, and (c) the unborn-HEAD gate closing a second data-loss path all re-measured against real git 2.51.1 and upheld. Seven non-blocking items; one worth a follow-up ticket: the tag trade is a P1 decision that no test holds, and the widening also reachesrefs/notesandrefs/bisect, which the doc comment does not name.