spawn_dev.py: add a --model flag, threaded through to the claude launch line nf-7nhw ← Beads

closed priority 2 task unassigned activesession:spawn-dev-model-flag

Twinned from https://github.com/cfsh/ninefold/issues/812 by tools/beads/import_github.py's reconcile pass.

Prescription, not a design question. Filed by the CTO — a gap in the CTO's own tooling, hit directly on 2026-08-10.

The gap

claude supports --model <model> (an alias — fable/opus/sonnet — or a full name like claude-sonnet-5), composes fine with --remote-control and --permission-mode, and is exactly how a CTO would pick which model does a chunk of coding work. tools/office/spawn_dev.py has no way to pass it through: launch_command() (spawn_dev.py:950) hardcodes argv = ["claude", "--remote-control", names.session, "--permission-mode", "auto"] with no model option, and main()'s argparse block (spawn_dev.py:~1692 onward) has no --model flag to plumb one in.

What this cost, concretely: spawning a dev on Sonnet for #795/#728 required the CTO to hand-reproduce the tool's own steps — worktree add -b, the NuGet.Config copy, the identity check, settings.local.json, and (to stay faithful) importing spawn_dev.py's own build_kickoff() rather than retyping the kickoff — solely to insert one flag on the final tmux new-session line. That is exactly the hand-transcription failure this tool exists to prevent (spawn-dev.md: *"this file used to transcribe the commands by hand and that transcription is what broke twice in one day"*), reintroduced through the one gap in the tool's own surface.

What to build

Add --model <model> to spawn_dev.py's argparse (main()), and thread it through to launch_command():

Board note this enables

Once shipped, the CTO's board rows for hand-spawned sessions (e.g. #795/#728's nf-dev-merge-reset-guard, which currently carries a *"Spawned by hand, not via spawn_dev.py"* caveat) can go back through the normal tool. That's a nice-to-have signal this issue exists, not a requirement of it.

Tests

Extend tools/tests/test_spawn_dev.py (or wherever launch_command()'s existing tests live):

  1. No --model passedargv/the joined command has no --model token, unchanged from today (regression guard, since this must not be a breaking change for every existing caller).
  2. --model sonnet passed → the launch command contains --model sonnet in the claude invocation, correctly shlex-quoted alongside everything else (reuse whatever the existing kickoff-quoting tests already exercise for apostrophes etc. — a model string won't need escaping, but don't special-case it either).
  3. --model composes with --resume → present in the resume launch command too.
  4. --dry-run output matches the real launch_command() result when --model is passed — this is the fidelity invariant above; assert the two are the same, not just that each independently "looks right".

Dependencies

None.

Comments

No comments.

Add a comment