feat(spawn): support batch task dispatch - #33
Merged
Conversation
fm-spawn.sh now accepts multiple id=repo pairs in one call, looping internally (in bash) and re-execing the proven single-task path per pair. This removes the need to hand-write multi-task dispatch loops in the tool shell (zsh), which does not word-split unquoted $vars and silently mangles ad-hoc loops. A shared --scout applies to all pairs; single-task invocations are unchanged. Adds behavior tests.
vipentti
pushed a commit
to vipentti/firstmate
that referenced
this pull request
Aug 5, 2026
* feat(spawn): batch dispatch via id=repo pairs fm-spawn.sh now accepts multiple id=repo pairs in one call, looping internally (in bash) and re-execing the proven single-task path per pair. This removes the need to hand-write multi-task dispatch loops in the tool shell (zsh), which does not word-split unquoted $vars and silently mangles ad-hoc loops. A shared --scout applies to all pairs; single-task invocations are unchanged. Adds behavior tests. * no-mistakes(review): Wire batch behavior tests into CI * no-mistakes(document): Document spawn batching
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
The captain asked to make bin/fm-spawn.sh support spawning several tasks in one call by accepting multiple id=repo pairs, removing the recurring need to hand-write multi-task dispatch loops in the tool shell. Motivation: the interactive tool shell is zsh, which (unlike bash) does not word-split an unquoted $var, so ad-hoc 'for ... in $pairs' / 'set -- $pair' loops silently mangled arguments and repeatedly broke multi-task dispatch. Decisions/tradeoffs: (1) chose unambiguous id=repo pair syntax so single-task invocations ( [harness|--scout|raw-cmd]) are detected as NOT batch and stay byte-for-byte unchanged - detection requires the first positional to contain '=' with a slash-free id part; (2) implemented batch as a re-exec of this same script in single-task mode per pair (via the FM_ROOT path, not $0) so the single-task path stays the one source of truth instead of duplicating spawn logic; (3) a shared --scout applies to every pair; (4) continue-on-failure - a failed pair is reported ('batch: FAILED to spawn ...') and skipped while the rest still launch, non-zero exit if any failed; (5) the watcher guard runs once per batch (not per pair) via an FM_SPAWN_NO_GUARD env flag on the re-execed children. Scope deliberately limited to this batch helper: the captain explicitly chose NOT to add a broader zsh word-splitting convention note elsewhere. Added tests/fm-spawn-batch.test.sh (behavior tests exercising arg routing only - each spawn fails fast at the missing-brief check before any tmux/treehouse side effect, so no real worktrees/windows are created) and documented the batch form in AGENTS.md section 7.
What Changed
bin/fm-spawn.shusingid=repopairs, with shared--scout, continue-on-failure behavior, and a single watcher guard for the batch.AGENTS.mdandREADME.md.Risk Assessment
✅ Low: Captain, the change is narrow, keeps single-task spawning as the source of truth, and the earlier CI coverage gap is now addressed.
Testing
Inspected the changed files, ran the targeted batch-spawn test and full behavior-test loop without lint/static analysis, captured CLI success and failure transcripts in the evidence directory, cleaned temporary
data,state, andprojectsfixtures from the worktree, and ended with no actionable test failures.Evidence: Targeted batch-spawn test output
ok - batch dispatch re-execs and reports every id=repo pair ok - a single id=repo pair routes through batch dispatch ok - single-task invocation (no '=') is untouched by batch detection ok - batch dispatch rejects an argument that is not id=repo ok - an arg whose id part contains '/' is not treated as a batch pairEvidence: All behavior tests output
Behavior test loop covering batch spawn and wake queue tests passed.Evidence: CLI batch spawn success transcript
$ PATH=<fake-tmux>:$PATH bin/fm-spawn.sh batch-a=projects/demo-a batch-b=projects/demo-b --scout spawned batch-a harness=codex kind=scout mode=direct-PR yolo=off window=firstmate:fm-batch-a worktree=/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KVNX6QWGFN889JRHRQTH6A1Z/fake-worktrees/firstmate_fm-batch-a spawned batch-b harness=codex kind=scout mode=local-only yolo=on window=firstmate:fm-batch-b worktree=/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KVNX6QWGFN889JRHRQTH6A1Z/fake-worktrees/firstmate_fm-batch-b exit=0Evidence: CLI batch failure transcript
$ FM_SPAWN_NO_GUARD=1 bin/fm-spawn.sh missing-a=projects/no-a missing-b=projects/no-b error: no brief at /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KVNX6QWGFN889JRHRQTH6A1Z/data/missing-a/brief.md batch: FAILED to spawn missing-a (projects/no-a) error: no brief at /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KVNX6QWGFN889JRHRQTH6A1Z/data/missing-b/brief.md batch: FAILED to spawn missing-b (projects/no-b) exit=1Evidence: Fake tmux calls from CLI success check
Fake tmux call log showing two batch children created windows, senttreehouse get, and launched codex from the normal single-spawn path.Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
tests/fm-spawn-batch.test.sh:1- The new batch-dispatch behavior test is not wired into the existing Behavior tests job, which still only runstests/fm-wake-queue.test.sh; this means the branch's main regression coverage will be skipped by CI and can silently rot. Addtests/fm-spawn-batch.test.shto the CI test step, or change the job to run all intended behavior tests.🔧 Fix: Wire batch behavior tests into CI
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
tests/fm-spawn-batch.test.shfor test_script in tests/*.test.sh; do "$test_script"; donebin/fm-harness.sh crewPATH=<fake-tmux>:$PATH bin/fm-spawn.sh batch-a=projects/demo-a batch-b=projects/demo-b --scoutwith temporary briefs/projects and capturedstate/*.metaplus fake tmux callsFM_SPAWN_NO_GUARD=1 bin/fm-spawn.sh missing-a=projects/no-a missing-b=projects/no-bFM_SPAWN_NO_GUARD=1 bin/fm-spawn.sh mixed-a=projects/no-a bogus-no-equalsgit status --short && find data state projects config -maxdepth 1 -type e 2>/dev/null | sort✅ **Document** - passed
✅ No issues found.
.github/workflows/ci.yml:18- Configured ShellCheck lint could not be run because shellcheck is not installed in PATH or common local locations; unresolved ShellCheck findings may remain.✅ **Push** - passed
✅ No issues found.