feat(dev): lab — per-instance worktrees + unified identity [worktrees 1/3] #134

Closed
opened 2026-06-08 00:29:48 +02:00 by dominik.polakovics · 0 comments

Parent

Cloonar/nixos#133 (superseded by this slice + siblings — the full settled design lives there).

What to build

Make every manual lab instance run in its own git worktree, and unify session identity on <project>~<label> (slots removed). Today all manual instances share the project's main checkout (handleStart passes dir = projectDir(project)); only AFK runs are isolated.

End-to-end:

  • Identity: session name = <project>~<label>, where <label> = afk-<N> (AFK) | <userlabel>-<timestamp> (manual labelled) | <timestamp> (manual unlabelled). Everything derives from <label>: kind (parseAFKLabel), branch (afk/<N> or lab/<label>), worktree dir (<project>-<N> for AFK [unchanged], <project>-<label> for manual). parseSessionName → split on the first ~. Delete allocateSlot/takenSlots/the Slot field (and AFK's "reserve slot 1" rule). Rows render: AFK → AFK #N; manual → label · 15:30 (time-only when unlabelled); the old slot-1 main rendering is gone. <timestamp> e.g. 20260608-1530, 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>. Any failure (no origin, fetch fails, ref unresolvable, add fails) aborts Start with the git cause in the banner and rolls back a partial worktree (mirror AFK's teardownClaim). Add a timeout so a network stall fails loudly. No fallback base.
  • Stop (manual path): guarded teardown — dirty worktree → keep worktree+branch; clean → remove worktree, delete branch iff merged into origin/<default> else keep. (AFK Stop is unchanged in this slice; reaper unification is the next slice.)
  • Docs: write docs/adr/00NN-lab-per-instance-worktrees.md (full design) and update CONTEXT.md's Instance entry (drop "only AFK runs are isolated…").

Reuse AFK's existing git.go worktree methods (AddWorktree / RemoveWorktree) where possible.

Acceptance criteria

  • Two concurrent instances of the same project run in separate worktrees and never share a working tree / index / branch.
  • Session identity is <project>~<label>; allocateSlot/takenSlots/Slot are gone; parseSessionName splits on the first ~.
  • Manual rows render label · 15:30 (time-only when unlabelled); AFK rows still render AFK #N.
  • Stopping a clean manual instance removes its worktree and keeps the branch only if unmerged; stopping a dirty one keeps both.
  • A repo with no usable origin (or a failing fetch) shows a clear Start error and leaves nothing behind.
  • ADR added; ADR-0007/0013 still accurate; CONTEXT.md "Instance" updated.
  • go test ./... passes (run locally — the eval-only pre-commit does not build/run Go).

Blocked by

None — can start immediately.

## Parent Cloonar/nixos#133 (superseded by this slice + siblings — the full settled design lives there). ## What to build Make every **manual** `lab` instance run in its own `git worktree`, and unify session identity on `<project>~<label>` (slots removed). Today all manual instances share the project's main checkout (`handleStart` passes `dir = projectDir(project)`); only AFK runs are isolated. End-to-end: - **Identity:** session name = `<project>~<label>`, where `<label>` = `afk-<N>` (AFK) | `<userlabel>-<timestamp>` (manual labelled) | `<timestamp>` (manual unlabelled). Everything derives from `<label>`: kind (`parseAFKLabel`), branch (`afk/<N>` or `lab/<label>`), worktree dir (`<project>-<N>` for AFK [unchanged], `<project>-<label>` for manual). `parseSessionName` → split on the first `~`. Delete `allocateSlot`/`takenSlots`/the `Slot` field (and AFK's "reserve slot 1" rule). Rows render: AFK → `AFK #N`; manual → `label · 15:30` (time-only when unlabelled); the old slot-1 `main` rendering is gone. `<timestamp>` e.g. `20260608-1530`, 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>`. Any failure (no `origin`, fetch fails, ref unresolvable, add fails) aborts Start with the git cause in the banner and rolls back a partial worktree (mirror AFK's `teardownClaim`). Add a timeout so a network stall fails loudly. No fallback base. - **Stop (manual path): guarded teardown** — dirty worktree → keep worktree+branch; clean → remove worktree, delete branch iff merged into `origin/<default>` else keep. (AFK Stop is unchanged in this slice; reaper unification is the next slice.) - **Docs:** write `docs/adr/00NN-lab-per-instance-worktrees.md` (full design) and update CONTEXT.md's **Instance** entry (drop "only AFK runs are isolated…"). Reuse AFK's existing `git.go` worktree methods (`AddWorktree` / `RemoveWorktree`) where possible. ## Acceptance criteria - [ ] Two concurrent instances of the same project run in separate worktrees and never share a working tree / index / branch. - [ ] Session identity is `<project>~<label>`; `allocateSlot`/`takenSlots`/`Slot` are gone; `parseSessionName` splits on the first `~`. - [ ] Manual rows render `label · 15:30` (time-only when unlabelled); AFK rows still render `AFK #N`. - [ ] Stopping a clean manual instance removes its worktree and keeps the branch only if unmerged; stopping a dirty one keeps both. - [ ] A repo with no usable `origin` (or a failing fetch) shows a clear Start error and leaves nothing behind. - [ ] ADR added; ADR-0007/0013 still accurate; CONTEXT.md "Instance" updated. - [ ] `go test ./...` passes (run locally — the eval-only pre-commit does not build/run Go). ## Blocked by None — can start immediately.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/nixos#134
No description provided.