feat(bin): make ship-branch prefix configurable per project - #2648
Open
wesleymatosdev wants to merge 3 commits into
Open
feat(bin): make ship-branch prefix configurable per project#2648wesleymatosdev wants to merge 3 commits into
wesleymatosdev wants to merge 3 commits into
Conversation
fm-brief.sh hardcoded every generated ship branch to fm/<task-id>, which leaks that firstmate produced the branch/PR - unwanted for a third-party public repo that does not use this tooling. Add an optional --branch-prefix flag to fm-brief.sh (default "fm/", so existing installs are unaffected) and teach fm-project-mode.sh - the registry's single-owner parser - to resolve a project's optional "branch=<prefix>" data/projects.md annotation via a new --branch-prefix query, order-independent with the existing mode/+yolo tokens. Firstmate resolves the override at task intake and passes it explicitly, mirroring how --mode already works; fm-brief.sh itself never reads the registry. An empty override resolves to a bare "<task-id>" branch rather than a leading slash. All five previously hardcoded fm/$ID sites (branch creation, never-push rule text, definition-of-done text, and the status message) now render the resolved prefix consistently.
…erge-local.sh hardcoded fm/ prefix
… in architecture.md
There was a problem hiding this comment.
Pull request overview
Captain, this PR makes the “ship branch” naming prefix configurable per project (via the data/projects.md registry parsed by bin/fm-project-mode.sh), so generated briefs and local-only merge tooling can use a non-fm/ prefix when desired while keeping the default behavior byte-identical (fm/<task-id>).
Changes:
- Adds
branch=<prefix>as an order-independent registry token and exposesbin/fm-project-mode.sh --branch-prefix <project>for mechanical resolution (defaulting tofm/, withbranch=meaning “no prefix”). - Adds
--branch-prefixtobin/fm-brief.shand uses one resolved$BRANCHconsistently across all brief sections that mention the branch. - Updates
bin/fm-merge-local.shto resolve the project’s branch prefix viafm-project-mode.sh --branch-prefixinstead of hardcodingfm/$ID, and extends tests/docs accordingly.
Reviewed changes
Copilot reviewed 4 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
bin/fm-project-mode.sh |
Extends registry parsing to support branch=<prefix> and a --branch-prefix query while preserving legacy <mode> <yolo> output. |
bin/fm-brief.sh |
Adds --branch-prefix and centralizes branch name rendering via a single $BRANCH variable. |
bin/fm-merge-local.sh |
Resolves branch prefix per project before locating the crewmate branch to fast-forward into the default branch. |
tests/fm-task-delivery.test.sh |
Adds coverage for fm-project-mode.sh --branch-prefix defaults, ordering, and empty override behavior. |
tests/fm-brief.test.sh |
Adds coverage that the resolved branch name is consistent across all generated brief sections and modes. |
docs/architecture.md |
Documents the registry/flag contract and intake behavior for branch-prefix resolution. |
AGENTS.md |
Updates the intake contract to resolve and pass --branch-prefix explicitly (default fm/ needs no flag). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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
Make the ship-branch prefix configurable per project instead of a global switch, because bin/fm-brief.sh hardcodes every generated crewmate branch to fm/ across ~5 sites (the branch-creation command, the never-push rule text, the definition-of-done text, and the status-message text), which leaks that firstmate (an agent-orchestration tool) produced the branch/PR - fine for firstmate own repo but unwanted noise for a third-party public OSS repo that does not use this tooling (e.g. screenpipe/screenpipe, guilhermerodz/input-otp), where the captain has had to manually rename a branch after the fact.
Design requirements:
Explicitly out of scope: do not touch .agents/skills/project-management/SKILL.md (a separate, already-open PR #2510 covers an unrelated change there, and these must ship as two fully independent PRs); do not retroactively rename branches on already-open PRs such as screenpipe-5622 (a separate, later decision).
This is a contribution to kunchenguid/firstmate, a repo the captain does not own/cannot push to directly; the gate is configured with fork https://github.com/wesleymatosdev/firstmate so branches push through the fork while the PR opens against origin.
What Changed
bin/fm-project-mode.shgains a--branch-prefix <project-name>query and a new order-independentbranch=<prefix>token in thedata/projects.mdregistry bracket annotation, defaulting tofm/when unset, unregistered, or the registry is absent.bin/fm-brief.shaccepts a new--branch-prefixflag (rejected on--scout/--secondmate, validated against spaces and a leading-) and resolves a singleBRANCHvalue used consistently across the branch-creation command, the never-push rule text, the definition-of-done text, and the status-message text for bothdirect-PRandlocal-onlymodes; it does not readdata/projects.mditself.bin/fm-merge-local.shresolves the project's registered branch prefix viafm-project-mode.sh --branch-prefix(falling back tofm/on failure) instead of hardcodingfm/$IDwhen locating the crewmate's branch to fast-forward.AGENTS.mdanddocs/architecture.mddocument the new intake step and the registry/flag contract; tests intests/fm-brief.test.shandtests/fm-task-delivery.test.shcover the defaultfm/<task-id>behavior, a configured prefix override, and consistency of the resolved branch name across all generated brief sections.Risk Assessment
✅ Low: Both round-1 findings are correctly resolved: AGENTS.md section 7 now instructs firstmate to resolve the registered branch prefix via
fm-project-mode.sh --branch-prefix <project>and pass it explicitly to fm-brief.sh, and fm-merge-local.sh now resolves the same value (via the identical PROJ_NAME=basename(PROJ) pattern already used in fm-spawn.sh) instead of hardcoding fm/$ID, with a safe fm/ fallback; traced the empty-prefix, unregistered-project, and missing-registry paths through fm-project-mode.sh and confirmed no wrong-branch or crash case remains reachable.Testing
Both targeted test suites covering the new --branch-prefix flag and registry field pass in full, and a manual end-to-end run confirms bin/fm-merge-local.sh's new registry-driven prefix resolution (untested by any automated suite) works correctly with a non-default override; no regressions or missing behavior found, and no findings to report.
Evidence: fm-brief.sh branch-prefix tests
Evidence: fm-project-mode.sh --branch-prefix resolution test
Evidence: manual end-to-end fm-merge-local.sh with a configured branch=fix/ override
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 2 issues found → auto-fixed ✅
AGENTS.md:281- Required design constraint from the intent: "firstmate resolves the per-project override at task intake, the same way it resolves delivery mode, and passes it to fm-brief.sh as an explicit --branch-prefix flag." AGENTS.md section 7 is the actual intake contract that instructs firstmate to do this for mode/yolo today (line 281: "Resolve every ship task's concrete delivery mode and yolo posture at intake, and pass both explicitly to the brief..."), but this diff does not touch AGENTS.md at all (confirmed: only bin/fm-brief.sh, bin/fm-project-mode.sh, and the two test files changed). Nothing in the codebase actually callsfm-project-mode.sh --branch-prefix <project>and forwards the result intofm-brief.sh ... --branch-prefix ...as part of normal task intake (grepped every bin/*.sh caller of fm-brief.sh; none pass --branch-prefix). As delivered, the new flag is fully mechanical and tested at the CLI layer, but nothing in the actual firstmate intake flow ever invokes it, so real ship tasks will keep getting the default fm/ prefix even for a project that registers a branch= override — the feature is inert in practice. Note .agents/skills/project-management/SKILL.md is explicitly out of scope per the intent, but AGENTS.md (which the intent's own language points to via "the same way it resolves delivery mode") is not excluded and appears to be the missing piece.bin/fm-merge-local.sh:44- Concrete reachable bug: bin/fm-brief.sh's local-only mode now renders the crewmate's branch as$BRANCH($BRANCH_PREFIX$ID), which can be any configured prefix (tests explicitly cover local-only + a custom prefix, e.g.contrib/$id). But bin/fm-merge-local.sh — the only script that performs the local-only merge, gated tomode=local-onlyat line 24 — still hardcodesBRANCH="fm/$ID"at line 44 and doesgit rev-parse --verify refs/heads/$BRANCHimmediately after. For a project with a registeredbranch=override running a local-only task, the crewmate correctly creates and commits to e.g.contrib/<id>(per the updated brief), but fm-merge-local.sh will look forfm/<id>, find nothing, and hard-error with "branch fm/<id> does not exist in <PROJ>", blocking the task's only merge path. state/<id>.meta recordsmode=but has no field for the resolved branch/prefix, so fm-merge-local.sh has no way to recover the actual branch name (unlike bin/fm-review-diff.sh, which falls back to the worktree's current branch viasymbolic-refwhenfm/$IDdoesn't exist — fm-merge-local.sh has no equivalent fallback). This is reachable today via a directfm-brief.sh --branch-prefix ... --mode local-onlyinvocation, independent of whether AGENTS.md intake wiring exists. Recommend resolving this at the same shared boundary fm-brief.sh already uses (bin/fm-project-mode.sh --branch-prefix <project>) rather than re-deriving it ad hoc, or recording the resolved branch into state/<id>.meta at spawn/brief time so every consumer (fm-merge-local.sh included) reads one source of truth instead of assumingfm/$ID.🔧 Fix: Wire branch-prefix intake in AGENTS.md; fix fm-merge-local.sh hardcoded fm/ prefix
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
bash tests/fm-brief.test.shbash tests/fm-task-delivery.test.shmanual: registered a project with[local-only branch=fix/]in a scratch data/projects.md, created afix/task123branch ahead of main in a scratch project repo, wrote a matching state/task123.meta, and ranbin/fm-merge-local.sh task123to confirm it resolves the registry's branch prefix (not the hardcoded fm/) and fast-forwards correctly✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.