feat(dev): global Claude model + effort selector for new lab sessions #157
No reviewers
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!157
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/156"
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?
Adds a global, persisted UI control that sets the Claude model and effort for every newly-spawned lab session — manual Start, New instance, and AFK runs. A change takes effect on the next spawn with no lab restart; already-running sessions keep the model fixed in their process, and the machine-wide interactive claude default (home-manager
settings.json) is left alone.What
spawn.go— the single server-side source of truth for the two closed allowlists: model family aliases (opus[1m]→"Opus (1M)",sonnet,fable,haiku) and effort levels (low,medium,high,xhigh,max), plusvalidateSpawnConfig. Family aliases replace the pinnedclaude-opus-4-8[1m]id so the list tracks the latest of each family and never goes stale.spawnSettings, with aSpawnConfig()getter that returns the documentedopus[1m]/maxdefaults when unset and a validatedSetSpawnConfigthat rejects out-of-allowlist values without persisting (a global bad value would otherwise break every future spawn).--model/--effortare appended fresh per spawn from an injected accessor wired toStore.SpawnConfig, read at spawn time. Both spawn paths (manualStartand the AFK base argv) go through it, so AFK is covered for free and the two can't drift. The login flow is unchanged.POST /spawn-config— validates both values via the store's setter, persists, and returns the standard#livefragment; a rejected value surfaces in the existing error banner and nothing is written.<select>s placed outside#live(poll-proof, same rationale as the filter input), auto-saving onchangethrough the existing intercepted-fetch path, with a no-JS Save fallback hidden once JS runs. Mobile-first: 44px native controls, 16px font (no iOS zoom).Verification
go test ./...,go vet ./..., andgofmtare clean (tmux-gated tests run with tmux present, as in the NixcheckPhase).changefires aPOST /spawn-configcarrying the chosen model+effort through the fragment fetch path; the selects keep their value across a morph.opus[1m]url round-trip), dynamic resolution inbaseStartArgv, and the render contract (options, pre-selected value, outside-#liveplacement).Closes #156
Validation — PASS
AFK contract: head
afk/156→main, body carries a validCloses #156; no conflicts (mergeable).Verification signal: this repo's only gate is the eval-only pre-commit dry-build, which does not compile or test lab's Go — so I ran the suite directly in an isolated worktree at the PR tip (
e3f74f6):gofmt -lclean ·go vet ./...clean ·go build ./...clean ·go test ./...→ok lab 42.8s(tmux-gated tests included; tmux present)go.mod/go.sum/*.nixchanges → no vendorHash or build-time risk; the embedded template is exercised by the render tests.Diff review against #156's Agent Brief:
opus[1m], Sonnet→sonnet, Fable→fable, Haiku→haiku; effortlow/medium/high/xhigh/max; nobest/opusplan/sonnet[1m].spawn.go) backs both the dropdown render and the validation.Sessions.startCmddrops the baked flags;--model/--effortare appended fresh per spawn via the injectedspawnConfigaccessor, and both manualStartand the AFK base argv route throughbaseStartArgv— AFK covered, paths can't drift (a test proves the read is at spawn time, not construction).omitemptyspawnkey;SetSpawnConfigvalidates before writing and rejects out-of-allowlist values without persisting; unset →opus[1m]+max.POST /spawn-configvalidates → persists → returns the#livefragment; a rejected value surfaces in the error banner.<select>s outside#live(poll-proof), auto-save on change via the existing fetch path, no-JS Save fallback hidden by JS; mobile-first (44px, 16px font).claudedefault untouched.Verdict: PASS. Awaiting free-text go-ahead to merge.