pub async fn fold_orphaned_worktrees(
runs: &Path,
worktrees_root: &Path,
home: &Path,
grace_secs: u64,
now: Timestamp,
) -> usizeExpand description
Reclaim worktrees under worktrees_root that no run record in runs
claims anymore, and return how many were removed.
fold_due only ever sees a worktree by walking runs/ first, so a
worktree whose run record is already gone — magi run rm, or a record
deleted before its worktree — never enters that loop at all: nothing there
is looking for it. This walks the worktree bay directly instead, and
removes any <short> directory that no run id maps to.
Two things must never happen, and this checks both before ever touching a directory:
- A worktree bay is never the only kind of thing under
worktrees_root, and this must not assume it is. A hand-placed scratch directory, or anything else an operator or another tool left in the same bay, has the same “no run claims it” shape as a genuine orphan but is not one - [looks_like_a_worktree_bay] is the same tag shapecrate::run::is_run_idalready requires of a real run’s short id, and anything else is left alone regardless of what else is true about it. - A worktree that was only just created might not have a
run.jsonyet for a reason that has nothing to do with being orphaned.Runner::startandRunner::reviewboth create the worktree before the firstRunState::savelands, and that gap - severalgitsubprocesses wide - is invisible tocrate::daemon::is_working_on_shortwhenever the run is not being driven through this daemon’s ownpollloop at all (amagi reviewinvocation, for one). A directory whose own modification time is withingrace_secsofnowis left alone on that basis alone, the same marginfold_duegives a run before treating it as truly finished - long enough that no realistic gap between aworktree addand itsrun.jsoncould ever be mistaken for one.
The one failure this must never cause is deleting the worktree of a run
that is genuinely in flight. crate::daemon::is_working_on_short is the
same liveness check fold_due trusts everywhere else in this module,
checked by short id because there is no full id to compare here; when it
cannot tell, this leaves the directory alone. Best-effort like the rest of
housekeeping: one directory git or the filesystem refuses to give up is a
tracing::warn, not a reason to abandon the rest of the pass.