Fixes mosaicstack/stack#454 Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
5.1 KiB
Hotfix Scratchpad — mosaic yolo <runtime> passes runtime name as initial user message
- Issue: fix(launcher): mosaic yolo runtime passes runtime name as initial user message (mosaicstack/stack#454)
- Branch:
fix/yolo-runtime-initial-arg - Type: Out-of-mission hotfix (not part of Install UX v2 mission)
- Started: 2026-04-11
Objective
Stop mosaic yolo <runtime> from passing the runtime name (claude, codex, etc.) as the initial user message to the underlying CLI. Restore the mission-auto-prompt path for yolo launches.
Root cause (confirmed)
packages/mosaic/src/commands/launch.ts:779 — the yolo <runtime> action handler:
.action((runtime: string, _opts: unknown, cmd: Command) => {
// ... validate runtime ...
launchRuntime(runtime as RuntimeName, cmd.args, true);
});
Commander.js includes declared positional arguments in cmd.args. For mosaic yolo claude:
runtime(destructured) ="claude"cmd.args=["claude"]— the same value
launchRuntime treats ["claude"] as excess positional args, and for the claude case that becomes the initial user message. As a secondary consequence, hasMissionNoArgs evaluates false, so the mission-auto-prompt path is bypassed too.
Live reproduction (intercepted claude binary)
$ PATH=/tmp/fake-claude-bin:$PATH mosaic yolo claude
[mosaic] Launching Claude Code in YOLO mode...
argv[1]: --dangerously-skip-permissions
argv[2]: --append-system-prompt
argv[3] (len=25601): # ACTIVE MISSION — HARD GATE ...
argv[4]: claude ← the bug
Non-yolo variant mosaic claude is clean:
argv[1]: --append-system-prompt
argv[2]: <prompt>
argv[3]: Active mission detected: MVP. Read the mission state files and report status.
Plan
- Refactor
launch.ts: extractregisterRuntimeLaunchers(program, handler)with an injectable handler so commander wiring is testable without spawning subprocesses.registerLaunchCommandsdelegates to it withlaunchRuntimeas the handler. - Fix: in the
yolo <runtime>action, passcmd.args.slice(1)instead ofcmd.args. - Add
packages/mosaic/src/commands/launch.spec.ts:- Failing-first reproducer: parse
['node','x','yolo','claude']and assert handler receivesextraArgs=[]andyolo=true. - Regression test: parse
['node','x','claude']asserts handler receivesextraArgs=[]andyolo=false. - Excess args: parse
['node','x','yolo','claude','--print','hi']asserts handler receivesextraArgs=['--print','hi'](with--printkept becauseallowUnknownOptionis true). - Excess args non-yolo: parse
['node','x','claude','--print','hi']assertsextraArgs=['--print','hi']. - Reject unknown runtime under yolo.
- Failing-first reproducer: parse
- Run typecheck, lint, format:check, vitest for
@mosaicstack/mosaic. - Independent code review (feature-dev:code-reviewer subagent, sonnet tier).
- Commit → push → PR via wrappers → merge → CI green → close issue #454.
- Release decision (
mosaic-v0.0.30) deferred to Jason after merge.
Framework compliance sub-findings (out-of-scope; to capture in OpenBrain after)
~/.config/mosaic/tools/git/issue-create.shusesevalon$BODY; arbitrary bodies with backticks,$, or parens break catastrophically.gitea_issue_create_apifallback usescurl -fsSwithout-L; after themosaicstack/mosaic-stack → mosaicstack/stackrename, the API redirect is not followed and the fallback silently fails.- Local repo
originremote still points at oldmosaic/mosaic-stack.gitslug. Not touched here per git-config safety rule. ~/.config/mosaic/TOOLS.mdreferenced by the global load order but does not exist on disk.
These will be captured to OpenBrain after the hotfix merges so they don't get lost, and filed as separate tracking items.
Progress checkpoints
- Branch created (
fix/yolo-runtime-initial-arg) - Issue #454 opened
- Scratchpad scaffolded
- Failing test added (red)
- Refactor + fix applied
- Tests green (launch.spec.ts 11/11)
- Baselines green (typecheck, lint, format:check, vitest — pre-existing
uninstall.spec.ts:138failure on branch main acknowledged, not caused by this change) - Code review pass (feature-dev:code-reviewer, sonnet — no blockers)
- Commit + push
- PR opened
- CI queue guard cleared
- PR merged (squash)
- CI green on main
- Issue #454 closed
- Scratchpad final evidence entry
Tests run
pnpm --filter @mosaicstack/mosaic run typecheck→ greenpnpm --filter @mosaicstack/mosaic run lint→ greenpnpm --filter @mosaicstack/mosaic exec prettier --check "src/**/*.ts"→ greenpnpm --filter @mosaicstack/mosaic exec vitest run src/commands/launch.spec.ts→ 11/11 passpnpm --filter @mosaicstack/mosaic exec vitest run→ 270/271 pass (1 pre-existinguninstall.spec.ts:138EACCES failure, confirmed on the branch before this change)pnpm typecheck(repo) → greenpnpm lint(repo) → greenpnpm format:check(repo) → green (after prettier-writing the scratchpad)
Risks / blockers
None expected. Refactor is small and the Commander API is stable. Test needs exitOverride() to prevent process.exit on invalid runtime.
Final verification evidence
(to be filled at completion)