lab: Manual AFK run via the per-project ⋯ menu #62
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#62
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?
Parent
#61
What to build
The end-to-end tracer for AFK runs. Add a per-project
⋯context menu (native inline<details>) whose first item is Start AFK run. On click,labclaims the lowest openready-for-agentissue from the project's own tracker and launches a seededclaude --remote-controlsession that resolves it in an isolated worktree and opens a PR. The run appears in the existing instances list badgedAFK #Nwith its deep link; manual Stop ends it. No auto-reaping and no automatic launching yet — those are Slices 2 and 3.Acceptance criteria
⋯button on each project card opens an inline native<details>menu — works with JS disabled, mobile-first (~44px target, single column).openstate across background refreshes (addopento the client-owned attribute skip-list alongsidevalue/checked/selected); verified with the ephemeral-jsdom oracle.originis undergit.cloonar.comthe menu shows Start AFK run; on a non-Forgejo project it shows a disabled "needs a git.cloonar.com repo" line. Detection is cached per project path.ready-for-agentissue viatea, flips its labelready-for-agent → in-progress(creating thein-progresslabel once if missing), and rolls the flip back if the spawn then fails.ready-for-agentissue exists, the action no-ops with a flash ("no ready-for-agent issues") and spawns nothing.labcreates a git worktree at~/.local/state/lab/worktrees/<project>~<N>on a new branchafk/<N>(root is outside the~/projects/scan root, so the scanner never lists it).labspawnsclaude --remote-control <project>~<slot>~afk-<N> --permission-mode auto --effort max \"<seed prompt with N>\"in that worktree; the deep link is captured exactly like an ordinary instance.AFK #N, with Open (deep link) and Stop.afk/<N>branch and any pushed commits survive.labsystemd service hasgitandteaon its PATH (hosts/fw/vms/dev/modules/lab/default.nix).afk/<N>, implement, verify, commit, push, open a PR withCloses #N).afk-<N>session-name encoding/parse, Forgejo detection).ready-for-agentissue and opens a PR withCloses #N; checked on a narrow screen.Blocked by
None - can start immediately.
Note: the "Stopping the run removes its worktree" criterion is an interim simplification for this tracer. Slice 2 (#63) sets the final semantics — a user-initiated Stop keeps the worktree and leaves the issue in
in-progressfor manual requeue; only a confirmed-success reap deletes the worktree.Agent Brief
Category: enhancement
Summary: Build the end-to-end tracer for AFK runs — a per-project ⋯ menu whose Start AFK run action claims the lowest open
ready-for-agentissue from the project's own Forgejo tracker and launches a seededclaude --remote-controlsession that resolves it in an isolated git worktree and opens a PR. Manual Stop only; no auto-reaping or automatic launching (those are #63/#64).Read ADR-0007 (
docs/adr/0007-lab-drives-afk-runs.md) and the AFK run / Instance (lab) glossary entries inCONTEXT.mdfirst — they are the locked design this brief implements. The code is thelabGo service (the dev-microvm session launcher).Current behavior:
lablists git projects under the projects root and runs one or moreclaude --remote-controltmux sessions per project, each shown as an instance row with Open (deep link) and Stop. Sessions are spawned through a tmux wrapper (Sessions, exposingStart/StartCommand(name, dir, argv)/Stop/CaptureURL); the base spawn argv isclaude --remote-control <name> --permission-mode auto --effort max— note the AFK-run flags already match the ordinary-instance default. Session identity is encoded bycomposeSessionName/parseSessionNameover aninstanceID{Project, Slot, Label}; a labelled instance takes slot ≥2, andsanitizeLabelrestricts a label to[A-Za-z0-9._-]. Per-spawn workspace trust is seeded viaSeedTrust. The UI is a server-rendered page progressively enhanced by an inline keyed-DOM morph; client-owned input state survives a background poll because the morph's attribute patcher skipsvalue/checked/selected. Project discovery (Scan/hasGitMarker) treats any directory with a.gitchild — directory or file — as a project. There is currently no notion of AFK runs, worktrees, Forgejo detection, ortea/gitshellouts.Desired behavior (all additive — ordinary human-driven instances are unchanged):
<details>/<summary>menu — functional with JS disabled, mobile-first (single column, ~44px targets). Its first item is Start AFK run.<details>openattribute to the morph's client-owned attribute skip-list — in both the copy pass and the removal pass, exactly asvalue/checked/selectedare handled. Verify with the ephemeral-jsdom oracle (per ADR-0004 there is no committed JS harness: drive a throwaway jsdom in /tmp, asserting withisEqualNodeplus an identity oracle).originremote is hosted ongit.cloonar.comis Forgejo-backed → the menu shows an enabled Start AFK run; any other project shows a disabled "needs a git.cloonar.com repo" line. Detection also yields theowner/repoused to targettea. Cache the result in-memory per project path (remotes don't change within a process lifetime; a restart re-derives cheaply).ready-for-agentissue, then claims it by flipping its labelready-for-agent → in-progress(creating thein-progresslabel on that repo once if it is missing).teamust be scoped to the target repo — e.g. by running it with the project directory as its working directory, which is howtearesolves the active repo. If the spawn then fails, roll the label flip back.ready-for-agentissue exists, spawn nothing and surface a brief, specific notice ("no ready-for-agent issues"), distinct from the generic action-error flash.git worktreeat<state>/lab/worktrees/<project>~<N>(where<state>is lab's per-user state dir, i.e. under~/.local/state) on a new branchafk/<N>, N being the claimed issue number. Base the branch on the freshly-fetched default branch (git fetch, then fork fromorigin/<default>), not the main checkout's current HEAD. The worktrees root is deliberately outside the projects scan root, soScannever lists a worktree as a bogus project (a worktree carries a.gitfile, whichhasGitMarkermatches).claude --remote-controlsession in that worktree by reusing the existing tmux-spawn path (Sessions.StartCommand) with the base argv plus the seed prompt appended as the trailing argument. Encode the run as a labelled instance carryingafk-<N>(session name<project>~<slot>~afk-<N>), so the existing name parser recovers the issue number. Seed trust on the worktree dir before spawning (as ordinary starts do). Capture the claude.ai deep link exactly like an ordinary instance.afk/<N>; implement the change; verify it the way the project expects (run its tests/build/linters if present); commit; push; and open a PR against the project's default branch with a conventional-commit-style title andCloses #Nin the body. Exact wording is yours; those are the required elements (per ADR-0007).afk/<N>branch and any pushed commits survive. (The final semantics — Stop keeps the worktree and parks the issue inin-progressfor requeue — are #63's job; do not implement them here.)labsystemd unit must havegitandteaon its PATH (addpkgs.gitandpkgs.teato the servicepath).Key interfaces:
Sessions— reuseStartCommand(name, dir, argv)for the AFK spawn (argv = base spawn argv with the seed prompt appended; the%s→name substitution it already performs still applies). No change to the tmux wrapper itself is expected.composeSessionName/parseSessionName/instanceID) — encode the run as a labelled instance withLabel = afk-<N>. Add a small pure helper that recognises anafk-<N>label and extracts N; unit-test it.teaandgit— wrap theteacalls (list issues, add/remove label, create label) and thegit fetch/git worktreecalls behind small interfaces, mirroring howSessionswraps tmux andAuthwrapsclaude auth. This keeps the selection/claim decision logic testable with a fake; the real shellouts are exercised only in live verification.originURL to(isForgejo, owner, repo); unit-test againstgit.cloonar.comand non-Forgejo origins.livetemplate — the instance view needs enough to render an AFK #N badge (derive it from theafk-<N>label, or carry an explicit flag/issue field). The card template gains the ⋯<details>menu with the conditional Start-AFK-run / disabled line.ok/failfetch-vs-redirect convention. The existing per-instance Stop handler must additionally remove the worktree when the stopped session is an AFK run (detected from itsafk-<N>label).SeedTrust— call on the worktree dir before the spawn.labNixOS unit,default.nix) — addpkgs.gitandpkgs.teato the unitpath.Storefields are required for this slice: the run's issue number lives in the session name, and Forgejo detection is in-memory.autoEnabled/consecutiveFailuresbelong to later slices.Acceptance criteria:
<details>menu; functional with JS disabled; mobile-first (~44px targets, single column).<details>openattribute); demonstrated via the ephemeral-jsdom oracle.git.cloonar.comproject the menu shows enabled Start AFK run; on any other project it shows a disabled "needs a git.cloonar.com repo" line; detection is cached per project path.ready-for-agentissue, flips its labelready-for-agent → in-progress(creatingin-progressonce if missing), and rolls the flip back if the spawn fails.ready-for-agentissue, the action spawns nothing and shows a specific "no ready-for-agent issues" notice.git worktreeis created at<state>/lab/worktrees/<project>~<N>on branchafk/<N>forked from the freshly-fetched default branch; the worktrees root is outside the scan root and never appears as a project card.claude --remote-control(base argv + seed prompt) in the worktree, with the issue number encoded in the session name; the deep link is captured like an ordinary instance; trust is seeded on the worktree dir.afk/<N>, implement, verify, commit, push, open a PR withCloses #N).afk/<N>branch and pushed commits survive. (Interim — see #63.)labsystemd unit hasgitandteaon its PATH.afk-<N>encode/parse, and Forgejo-origin detection. (Note: the repo pre-commit hook is eval-only and does NOT run Go tests — rungo test ./...in the module locally.)Out of scope (later slices / human steps):
in-progressfor requeue) → #63.autoEnabled/consecutiveFailures→ #65.Closes #N, narrow-screen check) and the one-timetea login addon dev are dominik's post-merge acceptance steps — the agent cannot deploy. Deliver the code, the Go unit tests, and the jsdom-oracle check on a branch with a PR, and stop there.