Skip to content

fix: validate fm-spawn positional arguments - #5048

Open
jcpoyser wants to merge 6 commits into
kunchenguid:mainfrom
jcpoyser:fm/fm-spawn-pos-unbound
Open

jcpoyser wants to merge 6 commits into
kunchenguid:mainfrom
jcpoyser:fm/fm-spawn-pos-unbound

Conversation

@jcpoyser

@jcpoyser jcpoyser commented Sep 20, 2026

Copy link
Copy Markdown

Intent

The developer wanted to fix bin/fm-spawn.sh so dispatches missing required arguments fail with a clear, actionable message naming the missing argument instead of a raw “POS[1] unbound variable” shell error. They required reproducing and confirming all reachable unguarded positional reads, including ship and secondmate paths, then adding explicit checks without changing argument grammar, defaults, or unrelated behavior. They also required a colocated regression test for a missing-project ship invocation that verifies nonzero exit, the clear message, and no unbound-variable text.

What Changed

  • Added early validation for task IDs, project directories, and batch pair fields before dispatch.
  • Updated batch re-execution to use validated task and project components.
  • Added regression coverage for actionable missing-argument errors and prevention of raw shell or launch errors.

Risk Assessment

✅ Low: The change is narrowly scoped to validating positional arguments before dispatch and lifecycle paths, with matching behavioral coverage and no material regressions identified.

Testing

Live CLI probes and targeted regression tests passed. Herdr lab provisioning was untestable because herdr is unavailable on PATH; no Herdr pass was claimed.

  • Live validation: ✅ go - 4 of 5 scenarios driven live against the product
Scenario Result Live Evidence
Ship spawn without <project-dir> reports an actionable error before dispatch-profile checks ✅ pass live Live positional validation transcript: ship-missing-project
Batch pairs with empty task or project fields fail before re-execution without raw shell errors ✅ pass live Live positional validation transcript: batch-empty-project, batch-empty-task, and mixed-batch preflight
Scout spawn without <project-dir> reports the actionable project-directory error ✅ pass live Live positional validation transcript: scout-missing-project
Secondmate invocation without a task id reports the actionable task-id error ✅ pass live Live positional validation transcript: secondmate-missing-task
Valid secondmate positional grammar remains accepted with an active dispatch profile ⏸️ untested no A real backend run was not performed; the Herdr lab could not be provisioned because the herdr executable is unavailable. Provide a verified Herdr executable on PATH and rerun through `bin/fm-herdr-…
Evidence: Live positional validation transcript

Source: Live positional validation transcript


$ ~/.no-mistakes/worktrees/df320aaf7596/01M3035WY7MQGZH7E5D35Z1WF5/bin/fm-spawn.sh live-missing-project-z1\ --mode\ no-mistakes\ --yolo\ off
exit=1
error: ship spawn requires a project directory positional argument (<project-dir>)

$ ~/.no-mistakes/worktrees/df320aaf7596/01M3035WY7MQGZH7E5D35Z1WF5/bin/fm-spawn.sh live-batch-z2=\ --mode\ no-mistakes\ --yolo\ off
exit=1
error: ship spawn requires a project directory positional argument (<project-dir>)

$ ~/.no-mistakes/worktrees/df320aaf7596/01M3035WY7MQGZH7E5D35Z1WF5/bin/fm-spawn.sh =projects/none\ --mode\ no-mistakes\ --yolo\ off
exit=1
error: spawn requires a task id positional argument (<task-id>)

$ ~/.no-mistakes/worktrees/df320aaf7596/01M3035WY7MQGZH7E5D35Z1WF5/bin/fm-spawn.sh live-scout-z3\ --scout
exit=1
error: scout spawn requires a project directory positional argument (<project-dir>)

$ ~/.no-mistakes/worktrees/df320aaf7596/01M3035WY7MQGZH7E5D35Z1WF5/bin/fm-spawn.sh --secondmate
exit=1
error: spawn requires a task id positional argument (<task-id>)

Assertions: all live positional-validation cases passed.

$ ~/.no-mistakes/worktrees/df320aaf7596/01M3035WY7MQGZH7E5D35Z1WF5/bin/fm-spawn.sh valid-batch-z4=projects/none invalid-batch-z5= --mode no-mistakes --yolo off
exit=1
error: ship spawn requires a project directory positional argument (<project-dir>)
mixed batch preflight assertion passed: no pair was re-executed.
Evidence: Herdr lab availability check

Source: Herdr lab availability check

$ bin/fm-herdr-lab.sh name positional-validation
fm-lab-positional-valid-18985-24040
$ bin/fm-herdr-lab.sh prepare fm-lab-positional-valid-18985-24040
exit=1
fm-herdr-lab: herdr is required
Herdr lab was not provisioned because the herdr executable is unavailable.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • ⚠️ bin/fm-spawn.sh:1354 - Positional checks occur after other guards. For example, a batch with an empty project and an active dispatch profile can fail at the harness backstop instead of naming the missing <project-dir>; single-task checks also occur after lifecycle and backend setup. Validate positional shape immediately after parsing to guarantee the promised actionable error.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 4 of 5 scenarios driven live against the product
Scenario Result Live Evidence
Ship spawn without <project-dir> reports an actionable error before dispatch-profile checks ✅ pass live Live positional validation transcript: ship-missing-project
Batch pairs with empty task or project fields fail before re-execution without raw shell errors ✅ pass live Live positional validation transcript: batch-empty-project, batch-empty-task, and mixed-batch preflight
Scout spawn without <project-dir> reports the actionable project-directory error ✅ pass live Live positional validation transcript: scout-missing-project
Secondmate invocation without a task id reports the actionable task-id error ✅ pass live Live positional validation transcript: secondmate-missing-task
Valid secondmate positional grammar remains accepted with an active dispatch profile ⏸️ untested no A real backend run was not performed; the Herdr lab could not be provisioned because the herdr executable is unavailable. Provide a verified Herdr executable on PATH and rerun through `bin/fm-herdr-…
  • bash tests/fm-spawn-batch.test.sh
  • Live isolated bin/fm-spawn.sh checks for missing ship/scout/task and empty batch arguments
  • Focused dispatch-profile and secondmate regression tests
  • bin/fm-herdr-lab.sh prepare availability check
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 1)
✅ **Push** - passed

✅ No issues found.

Closes #5044

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: whole thread read (body; no prior comments/reviews; linked open issue #5044 POS[1] unbound — another executor owns issue triage; tip vs main a09090d13ef24ce3cf71d171ade119896d6db301).

HEAD: e94072a68123c0c2dab92c06e82fba76162d1875
Attestation: MATCH — body binds e94072a68123c0c2dab92c06e82fba76162d1875.
workflow-zero: yes (no .github/workflows/*).
CI/NM: first-time fork runs for this HEAD approved this pass — CI 35527309684 + Require no-mistakes 35527309698/35527790977/35527739649 (4 approvals). NM now PASS on opened run; CI in progress / not green yet. no-mistakes remains blocking until CI completes green.

Contract-class: restore (own verdict; not author claim)

  • Main ship/scout branch reads PROJ=${POS[1]} under set -u with no guard → POS[1]: unbound variable when project-dir omitted (fm-spawn.sh aborts with "POS[1] unbound variable" when a ship or scout spawn omits the project directory #5044 shape; matches relaunch guard pattern already on main).
  • Tip refuses missing/empty task-id and project-dir (including empty batch id= / =proj pairs) with actionable errors before launch; valid spawn paths unchanged.
  • Author Closes #5044 treated as intent, not triage proof by itself — tip inspection matches the reported defect.

VISION (short):

  1. One captain/interface — aligns (clearer refusal, no new captain surface).
  2. Authority explicit — aligns (fail-closed on missing contract args; no consent widening).
  3. Scripts/mechanics — aligns (deterministic arg guards in spawn script).
  4. Restart non-event — aligns (no durability change).
  5. Delegation spine — aligns (refuses to guess missing spawn contract).
  6. Fleet outlives vendor — aligns (harness-agnostic spawn guards).
  7. Scope — aligns (command-layer spawn validation).
    Align/resist: aligns — restores promised usage-level refusal on a broken default path.

Outcome: waiting-ci. Auto-merge eligible once CI green (restore + MATCH + NM pass + safe review). No Firstmate flag. No competing PR opened for #5044.

Security FYI: none material (argument validation only; no workflow/RCE/credential surface).

@jcpoyser jcpoyser changed the title fix: reject missing spawn arguments with actionable errors fix: validate fm-spawn positional arguments Sep 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fm-spawn.sh aborts with "POS[1] unbound variable" when a ship or scout spawn omits the project directory

2 participants