Skip to content

feat: add per-project delivery modes - #14

Merged
kunchenguid merged 5 commits into
mainfrom
feat/project-modes
Jun 13, 2026
Merged

feat: add per-project delivery modes#14
kunchenguid merged 5 commits into
mainfrom
feat/project-modes

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

Intent

Add a per-project delivery mode plus an orthogonal yolo autonomy flag to firstmate, per the captain-approved plan at data/project-modes-p2/plan.md. Each project in data/projects.md gets a mode - no-mistakes (default), direct-PR, or local-only - chosen at project-add and recorded as '- [] - ' with optional '+yolo'. A new reader bin/fm-project-mode.sh parses it (legacy un-annotated lines and unknown modes/projects default safely to 'no-mistakes off' with a stderr warning). fm-spawn resolves and records mode= and yolo= into state/.meta alongside harness=/kind=. fm-brief shapes the ship definition-of-done by mode: no-mistakes -> /no-mistakes pipeline; direct-PR -> crewmate pushes + opens PR via gh-axi, no pipeline; local-only -> crewmate stops at 'ready in branch', firstmate reviews and merges to local main. fm-teardown gains a local-only carve-out: since local-only has no remote, the safety check requires the branch merged into the local default branch instead of 'on a remote'. New bin/fm-merge-local.sh performs the approved local-only merge as a clean fast-forward only (firstmate's merge gate-action; a deliberate, documented, narrowly-scoped exception to hard rule #1, only after captain/yolo approval). AGENTS.md sections 1, 2, 6, 7, 11 document the modes, the meta/registry format, conditional init (direct-PR/local-only skip no-mistakes init; local-only needs no remote), the per-mode lifecycle, and the yolo approval matrix (yolo on = firstmate approves routine decisions itself but still escalates destructive/irreversible/security-sensitive ones, never merges a red PR, and posts a post-hoc FYI). Deliberate decisions the captain made: yolo is orthogonal to mode (not a 4th mode) and offered-but-not-recommended; for local-only, firstmate (not the crewmate) performs the merge, hence the rule-#1 carve-out. Shell-only change; no test suite in this repo.

What Changed

  • Added per-project delivery mode parsing with safe defaults, including optional +yolo autonomy, and records mode=/yolo= in task metadata during spawn.
  • Updated task briefs and lifecycle handling for no-mistakes, direct-PR, and local-only delivery paths, including local-only teardown safety.
  • Added the approved local-only fast-forward merge helper and documented the registry format, mode behavior, initialization rules, and yolo approval matrix.

Risk Assessment

✅ Low: The remaining changes are bounded to documented delivery-mode handling and fail closed in the local-only merge/teardown paths, with no material merge-blocking issues found.

Testing

Bootstrap was silent, no automated test suite was present, and manual CLI-level validation showed registry parsing, brief shaping, spawn metadata, local-only merge safety, and local-only teardown behavior working as intended; all transient worktree fixtures were removed and the worktree ended clean.

Evidence: Clean project-mode CLI transcript
# fm-project-mode end-user outputs
legacyapp => no-mistakes off
gateapp => no-mistakes off
directapp => direct-PR on
localapp => local-only off
typoapp => warn: unknown mode "fast-lane" for typoapp; defaulting to no-mistakes off
no-mistakes off
missingapp => warn: project "missingapp" not in registry; defaulting to no-mistakes off
no-mistakes off

# fm-brief definitions of done by mode

## gateapp
# Definition of done
The task is complete only when committed on your branch.
When you believe it is complete, append `done: {summary}` to the status file and stop.
Firstmate will then instruct you to run /no-mistakes to validate and ship a PR.
During validation, fix auto-fix findings yourself; escalate ask-user findings per rule 6.
After /no-mistakes reports CI green, append `done: PR {url} checks green` and stop. You are finished.

## directapp
# Definition of done
This project ships **direct-PR**: you raise the PR yourself, without the no-mistakes pipeline.
The task is complete only when committed on your branch.
When it is implemented and committed, push your branch and open a PR with `gh-axi`, then append `done: PR {url}` to the status file and stop.
Do NOT run /no-mistakes. The captain reviews and merges the PR; firstmate relays it.

## localapp
# Definition of done
This project ships **local-only**: no remote, no PR, no pipeline.
The task is complete only when committed on your branch `fm/brief-localapp`. Do NOT push, do NOT open a PR, do NOT merge.
Keep your branch a clean fast-forward onto the current default branch - if `main` has advanced, rebase onto it so the eventual merge stays a fast-forward.
When it is implemented and committed, append `done: ready in branch fm/brief-localapp` to the status file and stop.
Firstmate then reviews your branch diff, the captain approves, and firstmate merges it into local `main`.

# fm-spawn records project mode metadata
spawned spawn-task harness=sh kind=scout mode=direct-PR yolo=on window=firstmate:fm-spawn-task worktree=/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV1B7XFB3K3TYGKSPSE9QFR4/projects/directapp-wt
worktree=/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV1B7XFB3K3TYGKSPSE9QFR4/projects/directapp-wt
project=/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV1B7XFB3K3TYGKSPSE9QFR4/projects/directapp
kind=scout
mode=direct-PR
yolo=on

# local-only merge and teardown gate
WARNING: tasks are in flight but no watcher has ever run (no liveness beacon).
Restart it NOW, before anything else: run bin/fm-watch.sh as a background task.
REFUSED: local-only worktree /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV1B7XFB3K3TYGKSPSE9QFR4/projects/localapp-wt has work not yet merged into main.
commits not yet on main:
d6c37cb local task change
Merge the branch into local main first (bin/fm-merge-local.sh after the captain approves), or get the captain's explicit OK to discard, then --force.
teardown refused before merge as expected
WARNING: tasks are in flight but no watcher has ever run (no liveness beacon).
Restart it NOW, before anything else: run bin/fm-watch.sh as a background task.
merged fm/local-task into local main (175cdaf -> d6c37cb) in /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV1B7XFB3K3TYGKSPSE9QFR4/projects/localapp
WARNING: tasks are in flight but no watcher has ever run (no liveness beacon).
Restart it NOW, before anything else: run bin/fm-watch.sh as a background task.
treehouse return --force /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV1B7XFB3K3TYGKSPSE9QFR4/projects/localapp-wt
teardown local-task complete (window firstmate:fm-local-task, worktree /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV1B7XFB3K3TYGKSPSE9QFR4/projects/localapp-wt)

# bootstrap optional config check
bootstrap silent success

worktree transient fixtures cleaned
- Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (9m24s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 3 issues found → auto-fixed ✅
  • 🚨 bin/fm-project-mode.sh:60 - Unknown modes do not fully fall back to the documented safe default: a registry entry like [typo +yolo] becomes no-mistakes on, enabling autonomous approvals even though the comment and requirements say unknown modes/projects default to no-mistakes off. Reset yolo=off in this fallback path.
  • 🚨 bin/fm-merge-local.sh:27 - DEFAULT is derived from the project checkout's current branch, so fm-merge-local.sh will fast-forward whatever branch happens to be checked out instead of verifying and merging into the real default branch/main. If projects/<repo> is on a feature branch, this silently lands the local-only task in the wrong place.
  • 🚨 bin/fm-teardown.sh:43 - The local-only teardown safety check also treats the project checkout's current branch as the default branch, so it can allow teardown after commits are merged into an arbitrary checked-out branch rather than local main/default. Resolve the actual default branch and fail closed if that ref cannot be determined.

🔧 Fix: Fix local-only safety defaults
✅ Re-checked - no issues remain.

🔧 **Test** - 1 issue found → auto-fixed ✅
  • 🚨 bin/fm-bootstrap.sh:41 - Running the documented bootstrap command in a fresh worktree where config/crew-harness is absent prints /config/crew-harness: No such file or directory instead of staying silent. AGENTS.md documents the file as optional, so first-time users hit an erroneous bootstrap warning before any project-mode work can be dispatched.
  • bin/fm-bootstrap.sh in the checked-out worktree with no config/crew-harness file present
  • With temporary data/projects.md fixtures: bin/fm-project-mode.sh nmproj, bin/fm-project-mode.sh directproj, bin/fm-project-mode.sh localproj, bin/fm-project-mode.sh missingproj, and bin/fm-project-mode.sh badproj
  • With temporary registry fixtures: bin/fm-brief.sh brief-nm nmproj, bin/fm-brief.sh brief-direct directproj, and bin/fm-brief.sh brief-local localproj, then verified the generated user-facing brief text for each delivery mode
  • With a temporary git project and harmless raw launcher: bin/fm-spawn.sh spawntask projects/spawnproj "printf crewmate-spawned", then verified state/spawntask.meta recorded mode=direct-PR and yolo=on
  • With a temporary local git project and sibling worktree: bin/fm-teardown.sh localtask before merge, bin/fm-merge-local.sh localtask, then bin/fm-teardown.sh localtask after merge using a fake treehouse shim
  • git status --short after cleanup to confirm transient worktree fixtures were removed

🔧 Fix: Guard optional harness config reads
✅ Re-checked - no issues remain.

  • bin/fm-bootstrap.sh
  • Created a temporary data/projects.md registry and ran bin/fm-project-mode.sh legacyapp gateapp directapp localapp typoapp missingapp to verify defaults, direct-PR +yolo, local-only, unknown-mode fallback, and missing-project fallback.
  • Ran bin/fm-brief.sh <id> <repo> for no-mistakes, direct-PR, and local-only registry entries and inspected the generated Definition of done sections.
  • Ran bin/fm-spawn.sh spawn-task projects/directapp 'sh -c sleep' --scout with worktree-local fake tmux to verify mode=direct-PR and yolo=on are recorded in task meta.
  • Created a temporary local git repo and worktree, ran bin/fm-teardown.sh local-task before merge to verify local-only teardown refusal, then ran bin/fm-merge-local.sh local-task and reran bin/fm-teardown.sh local-task with worktree-local fake treehouse to verify fast-forward merge then successful teardown.
  • Searched for existing test runner/test files with Glob **/*test*, Glob Makefile, and Glob package.json; none were present.
  • git status --short
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@kunchenguid
kunchenguid merged commit bed9bc2 into main Jun 13, 2026
3 checks passed
@kunchenguid
kunchenguid deleted the feat/project-modes branch June 13, 2026 21:56
vipentti pushed a commit to vipentti/firstmate that referenced this pull request Aug 5, 2026
* feat: per-project delivery modes (no-mistakes|direct-PR|local-only) + yolo flag

* no-mistakes(review): Fix local-only safety defaults

* no-mistakes(test): Guard optional harness config reads

* no-mistakes(document): Document project delivery modes

* no-mistakes: apply CI fixes
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.

1 participant