Found while resolving #477: the dangling HEAD symref turned out to be the small half, and this is the large half, recorded here so the trace is not lost. Not on map #444, because it is not a reclaim question.
The finding
metadata.json's default_branch is written exactly once and never revalidated. The only writers are repo_manager.rs:1240 (first bare clone) and :1267 (register_existing_bare). get_default_branch (repo_manager.rs:1664) short-circuits on the record, so the ls-remote --symref fallback that would notice a moved default is unreachable for any repository dl has already cloned.
After a remote deletes or renames its default branch:
dl owner/repo (no @branch) resolves to the dead name forever, via flows/launch.rs name_default_branch, workspace_clone.rs resolve_default_branch, and dl/src/target.rs:183. Workspace id, container, clone directory and tab title are all named after a branch the forge no longer has, and nothing ever prints "the default branch has moved".
- Before the sweep's
--prune removes the stale head from the bare, ensure_branch (workspace_clone.rs:606) answers BranchBase::Stale { NoBranchOnRemote } (a notice, not an error) and EnsureBranch::in_cache silently resurrects the deleted branch from the bare's copy.
- After the prune, the launch fails loudly, but the message names a git refusal (
EnsureBranchError::Branch), not the actual cause.
Why it is recorded rather than built
For the primary usage (own repositories, launches almost always by explicit branch) a deleted default branch is nearly a non-event, and the post-prune failure is at least loud. The fix is a policy question, when may dl rewrite a recorded fact, with blast radius across naming surfaces (target.rs, launch naming, workspace ids). The obvious shape when someone wants it: when the sweep's prune removes refs/heads/<recorded default> from the bare, refresh the record from the remote's HEAD in the same pass that noticed.
#477's reader fix covers the adopt path (a stale symref can no longer re-record a dead branch), so this ticket is only about records that already exist.
Found while resolving #477: the dangling HEAD symref turned out to be the small half, and this is the large half, recorded here so the trace is not lost. Not on map #444, because it is not a reclaim question.
The finding
metadata.json'sdefault_branchis written exactly once and never revalidated. The only writers arerepo_manager.rs:1240(first bare clone) and:1267(register_existing_bare).get_default_branch(repo_manager.rs:1664) short-circuits on the record, so thels-remote --symreffallback that would notice a moved default is unreachable for any repository dl has already cloned.After a remote deletes or renames its default branch:
dl owner/repo(no@branch) resolves to the dead name forever, viaflows/launch.rsname_default_branch,workspace_clone.rsresolve_default_branch, anddl/src/target.rs:183. Workspace id, container, clone directory and tab title are all named after a branch the forge no longer has, and nothing ever prints "the default branch has moved".--pruneremoves the stale head from the bare,ensure_branch(workspace_clone.rs:606) answersBranchBase::Stale { NoBranchOnRemote }(a notice, not an error) andEnsureBranch::in_cachesilently resurrects the deleted branch from the bare's copy.EnsureBranchError::Branch), not the actual cause.Why it is recorded rather than built
For the primary usage (own repositories, launches almost always by explicit branch) a deleted default branch is nearly a non-event, and the post-prune failure is at least loud. The fix is a policy question, when may dl rewrite a recorded fact, with blast radius across naming surfaces (
target.rs, launch naming, workspace ids). The obvious shape when someone wants it: when the sweep's prune removesrefs/heads/<recorded default>from the bare, refresh the record from the remote's HEAD in the same pass that noticed.#477's reader fix covers the adopt path (a stale symref can no longer re-record a dead branch), so this ticket is only about records that already exist.