fix(spawn): refuse direct-PR where CI requires no-mistakes PRs - #11
Merged
Merged
Conversation
added 3 commits
September 26, 2026 23:44
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 for a mechanical guard so this never happens again: a firstmate fix was dispatched with delivery mode direct-PR to a repository whose CI carries the "PR must be raised via no-mistakes" required check (.github/workflows/no-mistakes-required.yml). The directly opened PR failed that check and the whole change had to be re-run through the no-mistakes pipeline, costing about 30 minutes. The rule was already written in the supervisor's notes and in the project registry prose, but nothing enforced it. The guard: when firstmate dispatches a ship task with --mode direct-PR to a project whose repository requires PRs raised via no-mistakes, the dispatch refuses with a clear message naming the check and telling the supervisor to use --mode no-mistakes, instead of launching a worker whose PR is guaranteed to go red.
What Changed
bin/fm-spawn.shnow refuses a--mode direct-PRship spawn when the project's local checkout has a.github/workflows/*.yml|yamlfile containing thePR must be raised via no-mistakescheck. It exits with an error naming that workflow and telling the supervisor to use--mode no-mistakes. The check matches on the job name rather than the workflow filename, and needs no network access.resolve_project_dir_argmoved earlier in the script so the guard can use it, including forprojects/<name>aliases.tests/fm-task-delivery.test.shgains coverage for the new refusal.AGENTS.mdanddocs/architecture.mddocument the refusal in the delivery-mode guidance.Risk Assessment
✅ Low: The guard is a small, well-scoped pre-spawn refusal that now resolves the projects/ alias, is covered by a behavioral test that runs the real spawn script, and matches the stated intent.
Testing
I rantests/fm-task-delivery.test.sh, which passes, including the new test for this guard. I then ran the realbin/fm-spawn.shfrom/tmpagainst this repo, whose real.github/workflows/no-mistakes-required.ymlis present. I used an isolated home, and a fake tmux that exits non-zero so nothing is launched.--mode direct-PRwas refused (exit 1) with the workflow name and--mode no-mistakes, both by absolute path and by theprojects/<name>alias.--mode no-mistakes,--mode local-only, and--mode direct-PRon a project with no workflow all got past the guard and stopped only at the fake tmux. No spawn state was written. The transcript is saved as evidence and the worktree is clean.Evidence: Live fm-spawn transcript
Source: Live fm-spawn transcript
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:625- The guard reads workflows from the raw positional project argument (${POS[1]}), but fm-spawn treatsprojects/<name>as an alias resolved against$PROJECTS($FM_HOME/projects) byresolve_project_dir_arg(line ~2125, used at ~2305). Withprojects/fooand a cwd other than$FM_HOME,nm_attestation_workflowglobs a non-existentprojects/foo/.github/workflows/*.yml, returns empty, and the guard silently fails open. The spawn then proceeds with--mode direct-PR, and the PR fails the required check, which is the exact incident the change is meant to prevent. Fix: resolve the path the same way, either by movingresolve_project_dir_argabove this block or by inlining itsprojects/*mapping, before callingnm_attestation_workflow.🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
bash tests/fm-task-delivery.test.sh(includes the newtest_spawn_refuses_direct_pr_where_ci_requires_no_mistakes)Realbin/fm-spawn.shrun from/tmpagainst this repo's real.github/workflows/no-mistakes-required.yml, in an isolated FM_HOME with a fake tmux that exits non-zero: direct-PR by absolute path, direct-PR viaprojects/<name>alias, no-mistakes mode, local-only mode, direct-PR on a project with no workflow✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.