feat(dev): lab — per-instance git worktrees #133
Labels
No labels
bug
enhancement
in-progress
needs-info
needs-triage
p0
ready-for-agent
ready-for-human
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/nixos#133
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Every manual
labinstance of a project is spawned in the project's main checkout (~/projects/<project>):handleStartpassesdir = projectDir(project)for all instances, so two concurrent instances stomp each other's working tree, index, and branch. Only AFK runs are isolated today (CONTEXT.md: "only AFK runs are isolated from the project's main checkout").Goal: every instance — manual and AFK — runs in its own
git worktree, the way AFK runs already do.The design below was settled in full. This issue implements it; writing the ADR is the first task.
Design (settled)
Core model
origin/<default>.~/projects/<project>is demoted to the reference repo (scanner target + worktree parent + fetch/branch host); it is never an instance's cwd.~/projects/<project>is undisturbed.Identity — drop slots, unify on
<project>~<label><project>~<label>, where<label>isafk-<N>|<userlabel>-<timestamp>|<timestamp>.<label>: kind (parseAFKLabel), branch (afk/<N>orlab/<label>), worktree dir (<project>-<N>for AFK [unchanged],<project>-<label>for manual).parseSessionNamecollapses to split on the first~(project names and labels are both~-free after sanitising). DeleteallocateSlot,takenSlots, and theSlotfield; AFK's "take slot ≥2 to reserve slot 1" rule goes away.AFK #N(unchanged); manual →label · 15:30(time-only when unlabelled). The old slot-1 =mainrendering is removed.<timestamp>format e.g.20260608-1530(readable, sortable); existence-check-and-bump on a same-second collision.Start (synchronous, fail-loud)
auth check → git fetch origin → base origin/<default> → git worktree add -b lab/<label>-<ts> <wt> origin/<default> → SeedTrust(<wt>) → spawn in <wt>origin, fetch fails,origin/<default>unresolvable,worktree addfails) aborts Start with the git cause surfaced in the banner, rolling back any partial worktree (mirror AFK'steardownClaim).Teardown — ONE guarded rule everywhere
Applies to Stop, the AFK reaper (success and failure), startup orphan reconciliation, and the runtime sweep:
origin/<default>, else keep branch.AFK keeps only its outcome accounting: the consecutive-failure counter and the budget-clock neutrality (
afkRunsMu/ drop fromafkStarts) so a hand-Stop is never reaped as a death. The unmergedafk/<N>branch is kept on Stop, so the claim/park (ADR-0013) survives.Cleanup
<project>-<label>with no live<project>~<label>session) gets guarded teardown; every mergedlab//afk/branch + its worktree is deleted.git branch --merged origin/<default>after a best-effort fetch; auto-delete mergedlab/andafk/branches + their clean worktrees. Never touches dirty/unmerged.afk/<N>branches, which lab currently keeps forever.Parked-work view (new UI)
<details>"Parked" strip on the card (same idiom as the ⋯ menu), collapsed by default showing a cheap count (git for-each-ref refs/heads/lab refs/heads/afk+git worktree list).lab/andafk/, tagged by kind. Keep it off the ~4s fragment poll.afk/<N>deletes its claim branch → the issue becomes claimable again (a manual requeue; consistent with ADR-0013).Tasks
docs/adr/00NN-lab-per-instance-worktrees.mdcapturing the above; add a superseding note to ADR-0007 (manual Stop now applies guarded teardown to AFK runs; neutrality-on-failure unchanged). Update CONTEXT.md's Instance entry (drop "only AFK runs are isolated…").instance.go: drop slots;<project>~<label>scheme;parseSessionName= split-on-first-~; label =<userlabel>-<timestamp>/<timestamp>; helpers to derive branch + worktree path from a session name (AFK + manual).handlers.go/git.go/sessions.go: synchronous fail-loud worktree creation offorigin/<default>with rollback + timeout; spawn in the worktree;SeedTrust(<wt>).<details>+ cheap count on the poll path; lazy details endpoint; Discard action with confirm.~parsing, label/branch/worktree derivation, guarded-teardown decision table, orphan detection, merged detection, parked-view enumeration. Rungo test ./...locally — the eval-only pre-commit does not build/run Go.default.nix— bump theversiondate string.Acceptance
lab//afk/branches get GC'd at runtime.originshows a clear Start error rather than silently doing anything.afk/<N>, reap on PR/death/timeout, count failures, and treat a manual Stop as neutral.Out of scope
land-pr; merged branches auto-clean).Design settled via a
/grill-mesession; see CONTEXT.md ("Instance", "AFK run"), ADR-0007 (lab drives AFK runs), ADR-0013 (claim is the branch).Superseded by tracer-bullet slices #134 (per-instance worktrees + unified identity), #135 (unified teardown + worktree/branch cleanup), and #136 (parked-work view). The full settled design stays here for reference. Closing in favour of those.