Skip to content

feat(bin): make the ship-branch prefix configurable per project - #2648

Merged
kunchenguid merged 21 commits into
kunchenguid:mainfrom
wesleymatosdev:fm/branch-prefix-config
Sep 24, 2026
Merged

kunchenguid merged 21 commits into
kunchenguid:mainfrom
wesleymatosdev:fm/branch-prefix-config

Conversation

@wesleymatosdev

@wesleymatosdev wesleymatosdev commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

What Changed

  • Add an optional branch=<prefix> annotation to the data/projects.md registry with a --branch-prefix query in bin/fm-project-mode.sh (defaulting to fm/), and thread a --branch-prefix flag through fm-brief.sh, fm-spawn.sh, and fm-promote.sh so ship tasks create and record <prefix><task-id> branches; fm-dod-lib.sh renders the resolved branch in delivery contracts under a machine-readable Ship branch: line, and spawn refuses a brief/spawn branch mismatch while persisting branch= in task meta for relaunches.
  • Honor the recorded branch downstream: fm-merge-local.sh and fm-review-diff.sh read it from task meta (falling back to fm/<id> for old records and refusing invalid names), fm-fleet-snapshot.sh exposes it in task JSON, and fm-bearings-snapshot.sh maps PR head refs to task IDs via recorded branches instead of stripping the fm/ prefix (task list moved to a temp file to avoid E2BIG on large fleets).
  • Document the prefix across README, AGENTS.md, architecture/scripts/Gerrit docs, and the project-management skill, and add branch-prefix coverage to five test suites (CI's expected Bearings test count raised 59→60).

Risk Assessment

✅ Low: Every prior finding verifies as genuinely fixed with exact merge-local parity on the refusal, a mechanically correct slurpfile temp-file path, a header-consistent unknown-mode fallback, and real behavior-level regression tests; the sole residual is a direction-dependent wording inaccuracy in one advisory notice.

Testing

Round 1's only finding is resolved: commit 2df387e (added by the fix phase) skips the two real-tasks-axi tests when the installed tasks-axi predates 0.2.6, so fm-control-relaunch and fm-bearings-snapshot now exit 0 — the bearings suite runs to completion for the first time in this environment (it previously aborted), with the skips printing an explicit reason and the branch-prefix-specific tests in both suites passing. All focused suites for the branch's touched scripts pass (fm-brief, fm-task-delivery, fm-review-diff, fm-dod-lib, fm-fleet-snapshot-view), and a manual end-to-end CLI demo over a real generated brief and registry — captured in the transcript artifact — shows the configurable prefix resolving at intake, rendering consistently across all brief sections, keeping legacy fm/ and default posture output unchanged, and refusing misuse. Visual evidence is not applicable: the change is CLI/library shell surface, so the end-user experience is command output, captured verbatim in the artifacts.

Evidence: End-to-end CLI transcript: registry annotation -> intake resolution -> generated brief -> refusals

Source: End-to-end CLI transcript: registry annotation -> intake resolution -> generated brief -> refusals

=== End-to-end demo of the configurable ship-branch prefix (branch fm/branch-prefix-config) ===
Run against the worktree checkout at HEAD (2df387e), with a scratch firstmate home.
Every command shown was executed for real; output is verbatim.

=== 1. The captain's registry (data/projects.md in the demo home) ===

- upstream-lib [local-only branch=contrib/] - a third-party repo that must not read as firstmate-authored (added 2026-09-23)
- bare-branch-lib [direct-PR branch=] - ships on a bare task-id branch (added 2026-09-23)
- legacy-lib - default posture, no prefix recorded (added 2026-09-23)

=== 2. Firstmate resolves the registered prefix at task intake (fm-project-mode.sh --branch-prefix) ===

$ for p in upstream-lib bare-branch-lib legacy-lib; do ...; done
upstream-lib: mode+yolo=[local-only off] branch-prefix=[contrib/]
bare-branch-lib: mode+yolo=[direct-PR off] branch-prefix=[]
legacy-lib: mode+yolo=[no-mistakes off] branch-prefix=[fm/]

The token is order-independent, an empty branch= resolves to "" (bare <task-id> branch),
an unannotated project keeps the legacy fm/, and the default "<mode> <yolo>" output that
existing mechanical callers consume is unchanged for every project.

=== 3. Scaffold a ship task with the resolved override (local-only mode shown) ===

$ FM_HOME=<home> bin/fm-brief.sh task-e2e some-proj --mode local-only --branch-prefix 'contrib/'
scaffolded: <home>/data/task-e2e/brief.md (ship, mode=local-only; replace {TASK} and {FIRSTMATE_SPEC})

Branch references in the generated brief (verbatim grep):
22:1. First action: create your branch: `git checkout -b contrib/task-e2e --`
25:1. Never push to any remote and never open a PR. Work only on your `contrib/task-e2e` branch; firstmate handles the merge into local `main`.
78:Ship branch: contrib/task-e2e
80:The task is complete only when committed on your branch `contrib/task-e2e`. Do NOT push, do NOT open a PR, do NOT merge.
83:When it is implemented and committed, append `done [at=<epoch>]: ready in branch contrib/task-e2e` to the status file and stop.

The override renders in every generated section: branch-creation command, never-push rule,
status line, definition-of-done text, and status-message text. No fm/task-e2e remains.

=== 4. Stock install: no flag, no annotation -> legacy fm/<task-id> unchanged ===

$ FM_HOME=<home> bin/fm-brief.sh task-e2e-legacy some-proj --mode local-only
22:1. First action: create your branch: `git checkout -b fm/task-e2e-legacy --`
25:1. Never push to any remote and never open a PR. Work only on your `fm/task-e2e-legacy` branch; ...

=== 5. Empty override resolves to a bare <task-id> branch, no leading slash ===

$ FM_HOME=<home> bin/fm-brief.sh task-e2e-bare some-proj --mode local-only --branch-prefix ''
22:1. First action: create your branch: `git checkout -b task-e2e-bare --`
25:1. Never push to any remote and never open a PR. Work only on your `task-e2e-bare` branch; ...

=== 6. Misuse is refused loudly ===

$ bin/fm-brief.sh scout-task some-proj --scout --branch-prefix contrib/
error: --branch-prefix applies only to ship briefs; a scout makes no branch and a secondmate charter is not a delivery contract  (exit 1)

$ bin/fm-brief.sh bad-task some-proj --mode no-mistakes --branch-prefix 'bad prefix/'
error: --branch-prefix must not contain a space (got 'bad prefix/')  (exit 1)

=== 7. Review side: bearings maps a custom-prefix PR row back to its recorded task ===

Suite run (tests/fm-bearings-snapshot.test.sh), verbatim line from the captured log:
ok - --include-prs maps a custom branch-prefix PR back to its recorded task

=== 8. Relaunch side: a promoted scout with a recorded custom branch relaunches on it ===

Suite run (tests/fm-control-relaunch.test.sh), verbatim line from the captured log:
ok - fm-spawn --relaunch: a promoted scout with a recorded custom branch relaunches on it instead of being refused
Evidence: Full fm-bearings-snapshot suite run (previously aborted; now completes with clean tasks-axi floor skips; custom-prefix PR mapping passes)

Source: Full fm-bearings-snapshot suite run (previously aborted; now completes with clean tasks-axi floor skips; custom-prefix PR mapping passes)

ok - task teardown during metadata capture is omitted without aborting the snapshot
ok - current state and decision hints share one captured status observation
ok - reused live state is discarded when task generation changes
ok - large local snapshot overlaps local reads with byte-identical serial and concurrent projections
ok - remote ledgers collect concurrently under one budget, reuse aged cache, and cancel wedged collectors
ok - a missing remote ledger stays explicitly unreadable without remote summary computation
ok - Domain Alpha structured state overrides a stale parent Phase 7 event
ok - GNU stat file reads select -c without BSD filesystem-report pollution
ok - parent activity evidence is bounded and disclosed
ok - Bearings excludes a status-only child decision
ok - a structured child captain hold reaches Captain's Call
ok - missing, invalid, unreadable, malformed, and unavailable-child homes stay explicit unknowns
ok - oversized ledgers stay strict unknown
ok - secondmate and per-home child counts are bounded, disclosed, and explicitly expandable
ok - parent decisions remain untrusted contradiction evidence
ok - parent evidence reconciliation distinguishes matching holds, blocks, and decisions
ok - nonprogressing child states are explicit and inconsistent terminal rows invalidate
ok - registry unavailability and bounded truncation remain explicit
ok - repeated snapshots keep the same current landed baseline and ignore prior reports
ok - default output is bounded, local-only, and marks omitted surfaces
ok - TOON and JSON are parity representations of the same model
ok - landed includes secondmate-managed merges alongside main-home merges
skip: installed tasks-axi predates 0.2.6, so the real backlog mutations this regression needs are refused
skip: installed tasks-axi predates 0.2.6, so the real backlog mutations this regression needs are refused
ok - kindless v1 summaries retain report artifacts from fresh and cached ledgers
ok - default landed selection balances one dominant home with sparse homes
ok - landed selection refills capacity after sparse homes exhaust
ok - landed selection uses deterministic home order when homes exceed the cap
ok - landed selection preserves deterministic home and internal tie ordering
ok - landed selection handles no landed items
ok - --all-landed keeps the complete global landed output
ok - landed stays bounded with per-home + overall caps and omitted[] disclosure
ok - Bearings keeps a live blocker in structured live state and never converts it to Charted Next queue work
ok - action-free items (working/done/queued/landed) do not leak into Captain's Call
ok - main orphan in-flight stays out of Underway and is disclosed in omitted/gates
ok - main unstructured current is disclosed while structured siblings still project
ok - counterfactual meta clears main inventory warning and projects the live task
ok - working captain holds retain main and secondmate bucket surfaces
ok - active children reach Underway independently of a home captain hold
ok - blank legacy summary names use their durable identifier
ok - newest filed gates are selected before snapshot bounds
ok - Underway rows carry the durable task name and gates carry their filed date
ok - mixed secondmate roles, partial state, and captain readiness project independently
ok - main and secondmate captain actionability use the same blocker readiness
ok - a completed scout with decision-like report prose is a pointer, not pending
ok - an authoritative captain hold surfaces end-to-end
ok - current report pointers surface
ok - queued body prose never hides an item from the board
ok - --include-prs is the only path that fetches, and it enriches correctly
ok - --include-prs maps a custom branch-prefix PR back to its recorded task
ok - a partial GitHub failure degrades gracefully
ok - Perl fallback bounds stalled GitHub calls without coreutils timeout
ok - all fleet-sized sections are capped with counted opt-in expansion
ok - captain-held tasks of any kind reach Captain's Call, deferral is honored, and landed excludes answered calls
ok - structured hold buckets decide Captain's Call, Charted Next, and the reveal
ok - a blocked deferred hold stays gated when its date arrives and is revealed by --all-decisions
ok - revealed deferred holds display their deferral reason while live calls stay unannotated
ok - live PR enrichment caps repositories with counted expansion
ok - per-repository open-PR caps are disclosed with an expansion knob
ok - projection and TOON rendering failures exit nonzero with diagnostics
Evidence: Full fm-control-relaunch suite run (exit 0; two tasks-axi floor skips; custom-branch relaunch passes)

Source: Full fm-control-relaunch suite run (exit 0; two tasks-axi floor skips; custom-branch relaunch passes)

ok - fm-control relaunch: a same-harness relaunch replaces the agent in the same endpoint and worktree
ok - fm-control relaunch: pending composer text refuses before the exit command is typed
ok - fm-control relaunch: an unreadable composer fails safe before the exit command is typed
ok - fm-control relaunch: a linked spawning home preserves committed and unfinished work in the recorded copy
ok - fm-control relaunch: durable task metadata survives replacement launch publication
ok - fm-control relaunch: delivery and concurrent task metadata publication serialize
ok - fm-control relaunch: disabling tracing clears metadata and pane context
ok - fm-control relaunch: progress and the Firstmate-worktree worker identity reach the replacement
ok - fm-control relaunch: a ship task refuses without the progress note its replacement needs
ok - fm-control relaunch: switching harness is one ordinary relaunch, and the old wiring goes with the old agent
ok - fm-control relaunch: a harness switch resets model and effort unless they are named too
ok - fm-control relaunch: a prefixed recorded harness can switch adapters transactionally
ok - fm-control relaunch: a prefixed command requires an explicit replacement harness
ok - fm-control relaunch: a same-harness relaunch keeps the profile axes it was running with
ok - native Ultra relaunch preserves its profile and rejects an unsupported model before stopping
ok - fm-control relaunch: a signed-out worker account pin refuses before the old agent stops
ok - fm-control relaunch: the replacement follows the home's current worker account pin
ok - fm-control relaunch: explicit model and effort win over the recorded ones
ok - fm-control relaunch: refuses to relaunch onto an adapter with no verified mechanics
ok - fm-control relaunch: the retired incarnation's global turn-end token is revoked
ok - fm-control relaunch: wiring cleanup failure refuses replacement arming
ok - fm-control-lib: one owner resolves each harness's turn-end registry entry, and refuses a malformed token
ok - fm-control relaunch: a secondmate relaunch re-resolves its durable configured harness pin
ok - fm-control relaunch: invalid configured effort is ignored before stop
ok - fm-control relaunch: an adapter unverified for this task kind refuses before the agent is stopped
ok - fm-control relaunch: explicit secondmate harness resets unnamed profile axes
ok - fm-control relaunch: a ship task keeps its recorded harness instead of re-reading crew config
ok - fm-spawn --relaunch: with no explicit harness it reuses the task's recorded one, never the crew default
ok - fm-spawn --relaunch: a promoted scout with a recorded custom branch relaunches on it instead of being refused
ok - fm-promote/fm-spawn --relaunch: the current ship contract supersedes stale scout delivery text
ok - fm-spawn --relaunch: wiring armed under a prefixed harness name is still retired
ok - fm-spawn --relaunch: switching away from muse retires its session binding
ok - fm-spawn --relaunch: switching away from cursor retires its session binding
ok - fm-control relaunch: an unaccountable local copy refuses before the agent is touched
ok - fm-control relaunch: a worker with nothing to work from is never launched
ok - fm-control relaunch: a refusal before the agent is stopped leaves the durable record untouched
ok - fm-control relaunch: checkpoint inspection failures refuse before stopping
ok - fm-control relaunch: a launch failure after the stop keeps the prior record and reports the real state
ok - fm-control relaunch: unpublished rollback keeps concurrent durable metadata
ok - fm-control relaunch: post-publication failure keeps the new durable record
ok - fm-control relaunch: partial stop reconciles actual agent state
ok - fm-control relaunch: failed journal replacement preserves durable phase
ok - fm-spawn relaunch: prepublication abort removes replacement state
ok - fm-control relaunch: the checkpoint records the exact unlanded work it preserved
ok - fm-control relaunch: a secondmate's child work is accounted for and its charter is left alone
ok - fm-control relaunch: a secondmate home that is not this secondmate's is refused
ok - fm-control relaunch: unreadable child records fail checkpoint
ok - fm-control relaunch: unlistable state fails checkpoint
ok - fm-control relaunch: a vanished watcher scratch file does not fail the child-record checkpoint
ok - fm-control relaunch: two control actions on one task serialize instead of interleaving
ok - fm-spawn relaunch: direct entry participates in lifecycle serialization
ok - fm-promote: promotion participates in lifecycle serialization
ok - fm-spawn --relaunch: refuses to launch a second agent into a live endpoint
ok - fm-spawn --relaunch: symlinked records refuse before inspection
ok - fm-spawn --relaunch: keeps its early meta lock continuous
ok - fm-spawn --relaunch: pending closes refuse before replacement begins
ok - fm-spawn --relaunch: every identity axis comes from the record, and a contradicting flag refuses
ok - fm-spawn --relaunch: an unrecorded task is refused
ok - fm-spawn --relaunch: refuses to start a replacement outside the copy holding its work
ok - tmux: a window absent from its session refuses both verbs rather than being assumed gone
ok - tmux: an unfindable session refuses both verbs, so a live agent is never duplicated
ok - tmux: a dead server on this socket refuses both verbs rather than proving absence
ok - reclaim: an unclassifiable endpoint is still refused, so two agents cannot share one
ok - reclaim: a herdr pane that outlived its stopped server is adopted, never orphaned beside a new tab
ok - fm-control exit: a herdr pane that outlived its stopped server is already-stopped, not gone
ok - reclaim: a herdr rebind is created in the session the record names, never the ambient one
ok - reclaim: a herdr agent that came back with its server refuses, so one worktree keeps one agent
ok - reclaim: a herdr reclaim rebinds the endpoint and leaves the whole rest of the task alone
ok - reclaim: a herdr secondmate whose endpoint is gone is sent to its own respawn owner
ok - reclaim: a rebind refused from a plain shell reports the real cause, not a fabricated session mismatch
ok - skipped: installed tasks-axi predates 0.2.6, so dispatch refuses automatic backlog transitions
ok - skipped: installed tasks-axi predates 0.2.6, so dispatch refuses automatic backlog transitions
- Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (59m28s)

Pipeline

Updates from git push no-mistakes

⏭️ **intent** - skipped

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ⚠️ bin/fm-project-mode.sh:202 - The header (bin/fm-project-mode.sh:78-79) promises that an unknown mode under --branch-prefix falls back to "fm/", but the code prints the registered branch token instead: for a registry entry like - proj [no-mistake branch=fix/], fm-project-mode.sh --branch-prefix proj emits fix/ (exit 0) while the default output refuses-and-defaults the mode. The mode-typo warning fires, but the branch contract the header documents is not what runs; either the header must drop the claim or the unknown-mode fallback must also reset branch to "fm/". Which side is intended is an author decision.
  • ⚠️ bin/fm-bearings-snapshot.sh:299 - The new jq mapping passes the entire fleet snapshot as one argv element (--argjson tasks &#34;$SNAP&#34;). Linux caps a single exec argument at ~128KB (MAX_ARG_STRLEN), and the snapshot's default bounds make that reachable on a normal fleet: secondmate_current alone allows 256KB (FM_SNAPSHOT_SECONDMATE_MAX_BYTES) plus per-record 64KB parent-activity and 64KB registry sections; the bearings default path (line 241) includes secondmates. When exec fails with E2BIG, the || { nwarn++; continue; } at line 317 swallows it, so that repo's PR rows disappear from the bearings table entirely (not just unmapped task labels) with only a warning count - precisely on the largest fleets. Every neighboring jq call pipes SNAP via stdin; fix mechanically by piping instead of argv (e.g. write .tasks to a temp file once before the repo loop and pass --slurpfile).
  • ℹ️ bin/fm-review-diff.sh:76 - Unlike the other new readers of the meta branch= record (fm-merge-local.sh:99 and fm-spawn.sh:1731 both refuse an invalid recorded branch via check-ref-format), fm-review-diff.sh trusts it unvalidated: a hand-edited/corrupt branch= value fails the refs/heads lookup at line 78 and silently falls back to reviewing the worktree's moved HEAD with no warning - the exact wrong-content-diff outcome the feature's own test says must not happen. One mechanical refusal mirroring merge-local closes it.
  • ℹ️ bin/fm-spawn.sh:2902 - Spawn validates brief-vs-spawn branch agreement mechanically and reads the registry for forge (hard refusal) and mode (advisory deviation notice at line 2957), but performs no cross-check of the selected prefix against the registry's branch= token - not even an advisory notice. The only backstop is the agent following AGENTS.md section 7 at intake, so the feature's own stated purpose (a third-party project's branches/PRs not reading as fm/-authored) can silently fail for a task whose brief was scaffolded without the flag, discovered only at PR time. Cosmetic and consistent with pre-forge mode handling, but the asymmetry deserves a deliberate owner decision.
  • ℹ️ bin/fm-project-mode.sh:167 - Dead defensive code: forge is initialized to "none" (line 142) and only ever reassigned from a token matching /^forge=/ (line 156), so it can never be the empty string and this guard is unreachable. Removing it (or repurposing it for a reachable case) saves readers a hunt for the path where forge is empty.

🔧 Fix: Address remaining branch-prefix findings in four bin scripts
1 info still open:

  • ℹ️ bin/fm-spawn.sh:2968 - The new deviation notice's consequence clause "the task's branch and PR will read as firstmate-authored" is emitted unconditionally, but it is only true when the shipped branch carries the fm/ prefix. In the reverse deviation - e.g. fm-spawn --branch-prefix fix/ on an unregistered project (standing prefix resolves to the fm/ default) or a relaunch after the captain registered an override in the other direction - the notice correctly fires about the deviation but states a false consequence (fix/<id> does not read as firstmate-authored), which could mislead the operator's proceed/skip judgment. The clause also says the project "registers" the prefix even when it is the unregistered default (the sibling mode notice explicitly acknowledges that nuance in its comment). Condition the clause on the shipped branch actually starting with fm/, or phrase it neutrally (e.g. "differs from the registered prefix").
🔧 **Test** - 1 issue found → auto-fixed ✅
  • ⚠️ Two pre-existing tests fail in this environment for a reason unrelated to this branch: installed tasks-axi is 0.2.5 while the repo requires 0.2.6 (bin/fm-tasks-axi-lib.sh:45 FM_TASKS_AXI_MIN, unchanged by this branch). The failures are tests/fm-control-relaunch.test.sh "a relaunch must not re-run a transition the row already reflects" (backlog dispatch needs tasks-axi update/mv) and tests/fm-bearings-snapshot.test.sh "could not hold the answered captain question" (fm-captain-hold needs a compatible tasks-axi); the bearings suite aborts at that failure, so its branch-prefix test was verified separately by running a temporarily stripped copy of the suite (copy removed afterward). Both tests exist at the base commit and the author documented the environment-only failure in commit 3309cd2. Upgrading tasks-axi is a user-level tool change outside this worktree boundary, so this needs an operator decision: upgrade tasks-axi locally or rely on CI (which has 0.2.6) for those two suites.
  • bin/fm-test-run.sh tests/fm-brief.test.sh — all pass, incl. the five added --branch-prefix behaviors (legacy default, cross-section override, empty override, scout/secondmate refusal, value validation, shell-safe embedding)
  • bin/fm-test-run.sh tests/fm-task-delivery.test.sh — all pass, incl. fm-project-mode --branch-prefix order-independent resolution, brief/spawn branch agreement refusal, promote recording branch=fix/<id>, prefix-deviation advisory, merge-local registry immutability
  • bin/fm-test-run.sh tests/fm-review-diff.test.sh — all pass, incl. reviewing the meta-recorded custom ship branch and refusing a corrupt recorded branch
  • bin/fm-test-run.sh tests/fm-control-relaunch.test.sh — the added test 'a promoted scout with a recorded custom branch relaunches on it' passes; one unrelated pre-existing test fails on the tasks-axi 0.2.5 environment gap
  • bin/fm-test-run.sh tests/fm-bearings-snapshot.test.sh — aborts at a pre-existing tasks-axi-dependent test before reaching the new prefix test
  • isolated run of test_include_prs_is_the_only_fetch_path + test_include_prs_maps_custom_branch_prefix_to_task via a temporarily stripped copy of the bearings suite (copy deleted after) — 'a PR on a custom (non-fm/) branch prefix maps to its recorded task' passes
  • manual end-to-end captain-flow demo in a throwaway FM_HOME: registry annotation branch=contrib/ -> fm-project-mode.sh --branch-prefix resolves contrib/ (default fm/, empty -> bare id) -> fm-brief.sh scaffolds with contrib/<task-id> in branch creation, Ship branch line, never-push rule, DoD, and status message, zero fm/ leakage; stock project keeps fm/<task-id>; scout/space-prefix misuse refused with nothing scaffolded (transcript captured)

🔧 Fix: Skip real-tasks-axi tests below the repo's 0.2.6 floor
✅ Re-checked - no issues remain.

  • tests/fm-brief.test.sh — full suite, includes the new branch-prefix tests (legacy default, cross-mode override consistency, bare empty prefix, scout/secondmate refusal, space/dash validation, shell-metacharacter safety)
  • tests/fm-task-delivery.test.sh — full suite, covers fm-project-mode --branch-prefix order-independent resolution, fm-spawn prefix selection/deviation notice, fm-promote prefix propagation, fm-merge-local
  • tests/fm-review-diff.test.sh — full suite, covers meta-recorded custom-prefix branch review after worktree HEAD moves and corrupt-branch refusal
  • bash tests/fm-control-relaunch.test.sh — full suite (exit 0, 72 ok); verifies the 2df387e skip for tasks-axi < 0.2.6 and the custom-branch relaunch test
  • bash tests/fm-bearings-snapshot.test.sh — full suite (exit 0, 58 ok, 2 clean skips); verifies the suite no longer aborts and --include-prs maps a custom branch-prefix PR back to its recorded task
  • bash tests/fm-dod-lib.test.sh — full suite (script changed by the branch)
  • bash tests/fm-fleet-snapshot-view.test.sh — regression for the touched fm-fleet-snapshot.sh
  • Manual end-to-end demo: wrote a data/projects.md registry with branch=contrib/, branch=, and no annotation; ran bin/fm-project-mode.sh --branch-prefix &lt;project&gt; for each and the default two-word query; generated three briefs via bin/fm-brief.sh --mode local-only (override contrib/, no flag, empty prefix) and grepped the generated briefs to confirm every generated section renders the resolved branch and no fm/ leakage; exercised the scout and space-in-prefix refusals (exit 1 with named errors)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Copilot AI lite review requested due to automatic review settings August 19, 2026 21:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 exposes bin/fm-project-mode.sh --branch-prefix <project> for mechanical resolution (defaulting to fm/, with branch= meaning “no prefix”).
  • Adds --branch-prefix to bin/fm-brief.sh and uses one resolved $BRANCH consistently across all brief sections that mention the branch.
  • Updates bin/fm-merge-local.sh to resolve the project’s branch prefix via fm-project-mode.sh --branch-prefix instead of hardcoding fm/$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.

@greptile-apps

greptile-apps Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge.

Comment thread bin/fm-brief.sh
Comment thread bin/fm-merge-local.sh Outdated
@wesleymatosdev wesleymatosdev changed the title feat(bin): make ship-branch prefix configurable per project feat(bin): configure project ship branch prefixes Sep 9, 2026
wesleymatosdev added a commit to wesleymatosdev/firstmate that referenced this pull request Sep 10, 2026
…guid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

Diff review: optional project branch=<prefix> / --branch-prefix with default fm/ preserved; propagates through brief/spawn/promote/local-merge. Security clean (naming only).

VISION per-rule: One captain/interface — aligns when opt-in. Authority — aligns (config grant). Scripts vs agents — aligns. Restart — aligns (recorded branch). Delegation — aligns. Fleet/vendor — aligns. Scope — aligns.

contract-class: opt-in — unconfigured homes keep fm/; prefix only when the project registry sets it.

attestation: MISMATCH (body 5836391979e8569027644d010522f5eb1f15e6ef ≠ tip 2919c25130d1b740752a9c6a9aefc4c401ee041f). Please re-raise through no-mistakes so attestation matches this tip.
workflow-approval: fork CI + Require no-mistakes approved this tip (34423368096, 34423368085).
next: waiting on the author (attestation), not a captain hold. Not 14-day stale (author push today).

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

Re-triage on newer activity HEAD 254e76764a19c64ecaf69da89b5bcbbde106acb9 (merge-main after prior stamp on 2919c251). Default ship-branch prefix remains fm/; branch=<prefix> / --branch-prefix only when the project registry (or flag) sets it. Immutable branch= in task meta + merge-local consume that recorded value (Greptile's open merge-local P1 looks stale vs tip). Shell-injection tip from earlier review is addressed (git check-ref-format --branch + printf %q).

VISION per-rule:

  1. One captain, one interface — aligns when opt-in (registry/flag is the grant).
  2. Authority explicit — aligns (prefix never inferred from ambient state).
  3. Scripts own mechanics / agents own judgment — aligns (resolver + recorded contract).
  4. Restart is a non-event — gap: promoted scout with non-default prefix still relaunches from original scout brief.md (no Ship branch: line) while meta holds branch=fix/<id>, so branch agreement refuses relaunch — matches the body's open High risk / NM review residual.
  5. Delegation with a spine — aligns (intake resolves; workers follow recorded branch).
  6. Fleet outlives any vendor — aligns (naming contract, not vendor-specific).
  7. Scope — aligns (opt-in registry annotation; default fm/ unchanged).

contract-class: opt-in — unconfigured homes keep fm/<task-id>.

CI: tip checks still settling / UNSTABLE (CI run 34666742433 in progress at stamp time). NM tip: FAIL run 34666742457 — attestation head_sha 5836391979e8569027644d010522f5eb1f15e6ef ≠ PR head 254e76764a19. attestation: MISMATCH. mergeable: MERGEABLE/UNSTABLE. security: FYI tip only (injection hardening present; relaunch gap is functional correctness, not a captain security card).

next: waiting-author — re-raise through no-mistakes so attestation MATCHES this tip; close the promoted-scout relaunch gap (resolve relaunch instructions from the promoted ship contract / ship-instructions.md, not the scout brief). Do not merge (NM blocking + attestation MISMATCH + open High residual). Not 14-day stale (author push today). Prior workflow approvals on older tips do not clear this head's NM fail.

@wesleymatosdev

Copy link
Copy Markdown
Contributor Author

Thanks for the re-triage. Two responses to the tip `254e7676`:

Attestation MISMATCH: pushed the mechanical CI fixes (below) through `no-mistakes` from a fresh origin-pointed clone so the pipeline can re-attest this new tip; the run is currently stalled at the review step because the local pipeline's agent credentials (Claude Code OAuth session, and separately the Codex refresh token) are both expired and need an interactive re-login that I can't do from here. I'll re-push and let it re-attest as soon as one of those is renewed — flagging so this doesn't read as stalled-and-ignored.

CI fixes staged and verified locally (rebased clean onto current `main`, ShellCheck passes with the pinned 0.11.0):

  • `Lint` (SC2100 ×3 in `fm-brief.sh`/`fm-promote.sh`/`fm-spawn.sh`): quoted the `want_value=branch-prefix` assignment so ShellCheck stops misreading the literal string as arithmetic shorthand.
  • `Stock macOS Bash snapshot compatibility`: the PR adds a Bearings test, so the workflow's hardcoded assertion (`expected 59 Bearings tests`) was stale, not the feature — bumped to 60.

Greptile's open merge-local P1: agreed, that's stale vs this tip — the recorded `branch=` in task meta is what `fm-merge-local.sh` consumes now, not a reconstructed `fm/`.

The promoted-scout relaunch gap (the PR's own declared High risk, and the one open functional gap you flagged: a non-default-prefix promoted scout still relaunches from the original scout `brief.md`, which has no `Ship branch:` line, so branch agreement refuses relaunch: this is a real, separate behavior fix (select ship-instructions.md for a promoted-ship relaunch instead of the scout brief), not mechanical. Would you rather I fold that into this PR now, or land this PR's opt-in prefix contract first and open a focused follow-up for the relaunch-contract fix?
EOF
)

@wesleymatosdev wesleymatosdev changed the title feat(bin): configure project ship branch prefixes feat(bin): support configurable ship-branch prefix per project Sep 13, 2026
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: re-triage after author push + comment. HEAD c79c920afc57dfd8d01fbeebc8adb94f5cddd711 (CI lint/Bearings count fix + merge-main). Helps #1887 (prefix contract, not full custom name).

Default ship-branch prefix remains fm/; branch=<prefix> / --branch-prefix only when registry/flag sets it. Recorded branch= in task meta + merge-local/bearings/spawn consume it. .github/workflows/ci.yml delta is Bearings assertion count only (not a workflow RCE). Prior relaunch-from-scout-brief gap still open design call — not a merge blocker for the prefix contract itself.

Attestation: MATCH (body binds tip). Fork CI + NM were action_required on this tip — approved this pass: CI 34760317021, NM 34760337279 / 34760317043. MERGEABLE / BLOCKED until checks finish.

contract-class: opt-in — unconfigured homes keep fm/.

VISION.md per-rule

  • One captain, one interface — aligns when opt-in.
  • Authority is explicit — aligns.
  • Scripts own the mechanics — aligns.
  • A restart is a non-event — open gap (promoted scout relaunch from original scout brief still lacks Ship branch:) — track on this PR or a follow-up; does not flip class.
  • Delegation with a spine — aligns.
  • The fleet outlives any vendor — aligns.
  • Scope — aligns.

waiting-ci (was waiting-author for attestation; tip now MATCH). Merge-eligible N until tip CI+NM green. No captain card. Security FYI: none new (injection hardening already noted).

wesleymatosdev added a commit to wesleymatosdev/firstmate that referenced this pull request Sep 13, 2026
…guid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.
@wesleymatosdev wesleymatosdev changed the title feat(bin): support configurable ship-branch prefix per project feat(bin): make the ship-branch prefix configurable per project Sep 13, 2026
@wesleymatosdev

Copy link
Copy Markdown
Contributor Author

Autonomous re-gate status (run on the local no-mistakes pipeline, GLM lane via the Z.AI Anthropic endpoint — no OAuth dependency):

Attested tip: 1a32158a (current head). The pipeline completed rebase → review → test → document → lint → push → PR on this head (run 01M2DNC6N1BHND3H0Y2KS01X0A): rebase onto current main resolved 4 conflicts, review landed two verified auto-fixes (bin/fm-spawn.sh relaunch tolerance for promoted scouts, bin/fm-review-diff.sh now reads the recorded branch= meta contract with regression tests), focused test suites, docs, and ShellCheck all green. Body attestation binds 1a32158a. One residual auto-fix warning remains open on that run (fm-review-diff's header contract text not updated to match its new resolution order) — disclosed, not blocking.

Fork CI + Require-no-mistakes on 1a32158a: action_required — queued for maintainer workflow approval (the author account cannot approve fork-PR workflow runs). This is an external block, not a failure; the same block was cleared by maintainer approval on the previous tip.

Promoted-scout relaunch fix (5c60760a) is not yet attested, and we are not going to force it: the author tip 5c60760a (relaunch a promoted ship task from its ship contract, per the recorded Ship branch contract, with regression tests) and the gate's own fix round ended on divergent lineages after a rerun reused a stale run record. A reconciliation merge (8231f086, tree identical to 5c60760a) was pushed through the gate for re-review; its review round returned two findings we will not self-approve, so that run was cancelled and the decisions are escalated verbatim:

  1. relaunch-refuses-direct-spawn-custom-prefix-ships (error, bin/fm-control.sh:813, ask-user): "fm-control relaunch refuses every directly-spawned ship task on a custom-prefix project, on a false premise. Trace: ship spawn on a project registered branch=contrib/ records branch=contrib/<id> in meta (bin/fm-spawn.sh:3951) and its brief.md carries the matching Ship branch: contrib/<id> line (bin/fm-dod-lib.sh); ship-instructions.md does not exist (only bin/fm-promote.sh writes it). On relaunch, fm-control.sh:805 finds no ship-instructions.md, :807-813 then dies claiming "the scout brief cannot satisfy the ship branch agreement" — but bin/fm-spawn.sh:2450 extracts Ship branch: from brief.md and it matches the recorded branch, so the spawn would succeed … Net effect: the first crashed worker on any custom-prefix project is unrecoverable through the operator relaunch entry point … Fix at the shared boundary: in fm-control's instruction selection, accept $DATA/$ID/brief.md when its Ship branch: line equals the recorded branch (mirroring fm-spawn's agreement), and die only when the brief genuinely cannot satisfy it (promoted tasks whose ship contract is missing)."

  2. review-diff-ignores-recorded-branch (warning, auto-fix): the reconciliation tree dropped cdcb318's meta-first branch= read in bin/fm-review-diff.sh — this fix is already present and attested on the current head 1a32158a; only the reconciled variant lost it.

In short: 1a32158a is gated, attested, mergeable, and waiting only on maintainer workflow approval. Whether to land the tip-content variant (5c60760a/8231f086) — which resolves the relaunch gap a different way — needs the design call in finding 1 first. Nothing was merged, no PR body hand-edits, no pushes outside the pipeline.

wesleymatosdev added a commit to wesleymatosdev/firstmate that referenced this pull request Sep 17, 2026
…guid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.
@wesleymatosdev

Copy link
Copy Markdown
Contributor Author

Rebased onto current upstream main (was 33 behind, head 1a32158a → b071bc9d, 11 commits). Conflict resolutions:

  • bin/fm-brief.sh / bin/fm-dod-lib.sh: upstream aa92177 introduced fm_ship_rule_one() as the single owner of rule 1; the branch now passes the resolved branch name through it (fm_ship_rule_one "$MODE" "$ID" "$BRANCH") instead of its pre-refactor inline case, keeping both the single-owner refactor and the configurable prefix.
  • bin/fm-promote.sh: kept upstream's promote_delivery_contract + PROMOTION_SHIP_SPEC structure; the promotion ship-spec branch-creation step now renders the configured $BRANCH instead of hardcoded fm/$ID.
  • bin/fm-spawn.sh: merged --branch-prefix intake/relaunch-refusal into upstream's current parser and relaunch-guard shape.
  • tests/fm-control-relaunch.test.sh: both tests kept — upstream's test_promoted_scout_relaunch_receives_the_current_delivery_contract and the branch's test_spawn_relaunch_of_promoted_scout_uses_the_recorded_branch.

Gates re-run on the new tip: fm-brief.test.sh (31 ok), fm-task-delivery.test.sh (all pass), fm-spawn-batch.test.sh (all pass), fm-spawn-dispatch-profile.test.sh (all pass), bash -n on all touched scripts, shellcheck -x clean (zero warnings/errors; fixed an SC2100 the rebase surfaced and dropped a now-unused BRANCH_Q). Full no-mistakes pipeline re-attestation can run on this tip if required.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

Overlap: #4384 (JoeyRichter) is CLOSED not merged (author closed 2026-09-14 in favor of this PR). No leftover to close; #2648 remains the live opt-in prefix PR. Tip 69d660ad still has no --branch-prefix / branch= project annotation on main.

Diff review on HEAD b071bc9d90f8a58560fdb88d998b00b8f107d6ec: default ship prefix stays fm/; branch=<prefix> / --branch-prefix only when set. Promoted-scout relaunch gap from prior stamp looks addressed (meta-recorded branch wins when brief lacks Ship branch:; test test_spawn_relaunch_of_promoted_scout_uses_the_recorded_branch). Workflow delta is Bearings expected-count 59→60 only — not a gate weaken. Security clean (naming + git check-ref-format).

VISION per-rule:

  1. One captain, one interface — aligns when opt-in.
  2. Authority explicit — aligns (registry/flag grant; never inferred).
  3. Scripts own mechanics — aligns (resolver + recorded meta).
  4. Restart non-event — aligns on tip (relaunch uses recorded branch).
  5. Delegation with a spine — aligns.
  6. Fleet outlives vendor — aligns.
  7. Scope — aligns (opt-in registry annotation).

contract-class: opt-in.
attestation: MISMATCH (body 1a32158af35093f0562b35335884b0d32c5bf424 ≠ tip b071bc9d). Please re-raise through no-mistakes so attestation MATCHES this tip.
CI/NM: fork runs approved this pass — CI 35184740015, NM 35184739999. mergeable MERGEABLE but mergeStateStatus BLOCKED (workflow file + ruleset) until a scoped land or count lands separately — still blocked first on attestation/CI.
next: waiting-author (attestation). Not 14-day stale (author push today). Firstmate-flag no (not otherwise-ready).

@wesleymatosdev

Copy link
Copy Markdown
Contributor Author

Following up on the triage note — this PR has been re-raised end-to-end through the no-mistakes pipeline (run 01M2R4TDV4V7Z1977HPXXXP2PZ), per the request that the attestation match the tip.

Current state at head 236ad0bf (advanced from the originally submitted b071bc9d by three pipeline-owned commits: a review-fix restoring %q branch escaping in promotion instructions with a regression test, a documentation pass, and a ShellCheck SC2016 fix):

Could you approve the workflow runs when you get a chance? Happy to re-run or rebase if anything else is needed.

wesleymatosdev added a commit to wesleymatosdev/firstmate that referenced this pull request Sep 23, 2026
…guid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.
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.
…guid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.
…snapshot count

- Quote the --branch-prefix want_value assignment in fm-brief.sh, fm-promote.sh,
  and fm-spawn.sh so ShellCheck SC2100 no longer misreads the plain string
  'branch-prefix' as arithmetic shorthand.
- Bump the Stock macOS Bash snapshot job's hardcoded Bearings test-count
  assertion from 59 to 60: this PR added a Bearings test, so the count was
  stale, not the feature.
fm-review-diff.sh's header is the owner of its branch-resolution
contract; it still described only the legacy local-branch behavior
after the change made review-diff honor state/<id>.meta's recorded
ship branch. README's feature bullet enumerates the registry's
optional flags and was missing the new branch=<prefix> override.
…x override test

The rebase resolution of tests/fm-brief.test.sh kept the branch's
pre-merge \`done: ready in branch ...\` assertion while the merged
fm-dod-lib.sh (carrying main's epoch-stamped status line) renders
\`done [at=<epoch>]: ready in branch ...\`. Align the assertion so the
override-consistency test matches the behavior it verifies.
@kunchenguid
kunchenguid merged commit 67130f1 into kunchenguid:main Sep 24, 2026
19 checks passed

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: this is merged. Thank you @wesleymatosdev — really appreciate you taking the time on this.

mituso89 pushed a commit to mituso89/firstmate that referenced this pull request Sep 26, 2026
…henguid#2648)

* feat(bin): make the ship-branch prefix configurable per project

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.

* no-mistakes(review): Wire branch-prefix intake in AGENTS.md; fix fm-merge-local.sh hardcoded fm/ prefix

* no-mistakes(document): docs: document configurable ship-branch prefix in architecture.md

* no-mistakes(review): Persist immutable branch contracts

* no-mistakes(document): Document configurable ship branch prefixes

* no-mistakes(lint): Captain: fix ShellCheck test warnings

* fix(bin): map bearings PR rows to their recorded ship branch (kunchenguid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.

* fix(ci): align lint arithmetic-looking assignment and stale Bearings snapshot count

- Quote the --branch-prefix want_value assignment in fm-brief.sh, fm-promote.sh,
  and fm-spawn.sh so ShellCheck SC2100 no longer misreads the plain string
  'branch-prefix' as arithmetic shorthand.
- Bump the Stock macOS Bash snapshot job's hardcoded Bearings test-count
  assertion from 59 to 60: this PR added a Bearings test, so the count was
  stale, not the feature.

* no-mistakes(review): fix(bin): honor recorded ship branch in relaunch and review-diff

* no-mistakes(document): docs: complete branch-prefix flag in brief and promote headers

* fix(lint): quote branch-prefix parser token; drop unused BRANCH_Q after rebase

* no-mistakes(review): Restore %q branch escaping in promotion instructions with regression test

* no-mistakes(document): document recorded ship branch and prefix flag

fm-review-diff.sh's header is the owner of its branch-resolution
contract; it still described only the legacy local-branch behavior
after the change made review-diff honor state/<id>.meta's recorded
ship branch. README's feature bullet enumerates the registry's
optional flags and was missing the new branch=<prefix> override.

* no-mistakes(lint): Silence SC2016 on intentional single-quoted sed expression

* no-mistakes(review): address branch-prefix review findings in DoD and project-mode

* no-mistakes(test): branch-prefix suites pass under tasks-axi 0.2.6; environment-only failure

* no-mistakes(document): purge stale fm/ branch naming from docs and headers

* fix(test): assert the merged epoch status wording in the branch-prefix override test

The rebase resolution of tests/fm-brief.test.sh kept the branch's
pre-merge \`done: ready in branch ...\` assertion while the merged
fm-dod-lib.sh (carrying main's epoch-stamped status line) renders
\`done [at=<epoch>]: ready in branch ...\`. Align the assertion so the
override-consistency test matches the behavior it verifies.

* no-mistakes(review): Address remaining branch-prefix findings in four bin scripts

* no-mistakes(test): skip real-tasks-axi tests below the repo's 0.2.6 floor

* no-mistakes(document): document spawn's branch-prefix registry deviation notice
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 26, 2026
…henguid#2648)

* feat(bin): make the ship-branch prefix configurable per project

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.

* no-mistakes(review): Wire branch-prefix intake in AGENTS.md; fix fm-merge-local.sh hardcoded fm/ prefix

* no-mistakes(document): docs: document configurable ship-branch prefix in architecture.md

* no-mistakes(review): Persist immutable branch contracts

* no-mistakes(document): Document configurable ship branch prefixes

* no-mistakes(lint): Captain: fix ShellCheck test warnings

* fix(bin): map bearings PR rows to their recorded ship branch (kunchenguid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.

* fix(ci): align lint arithmetic-looking assignment and stale Bearings snapshot count

- Quote the --branch-prefix want_value assignment in fm-brief.sh, fm-promote.sh,
  and fm-spawn.sh so ShellCheck SC2100 no longer misreads the plain string
  'branch-prefix' as arithmetic shorthand.
- Bump the Stock macOS Bash snapshot job's hardcoded Bearings test-count
  assertion from 59 to 60: this PR added a Bearings test, so the count was
  stale, not the feature.

* no-mistakes(review): fix(bin): honor recorded ship branch in relaunch and review-diff

* no-mistakes(document): docs: complete branch-prefix flag in brief and promote headers

* fix(lint): quote branch-prefix parser token; drop unused BRANCH_Q after rebase

* no-mistakes(review): Restore %q branch escaping in promotion instructions with regression test

* no-mistakes(document): document recorded ship branch and prefix flag

fm-review-diff.sh's header is the owner of its branch-resolution
contract; it still described only the legacy local-branch behavior
after the change made review-diff honor state/<id>.meta's recorded
ship branch. README's feature bullet enumerates the registry's
optional flags and was missing the new branch=<prefix> override.

* no-mistakes(lint): Silence SC2016 on intentional single-quoted sed expression

* no-mistakes(review): address branch-prefix review findings in DoD and project-mode

* no-mistakes(test): branch-prefix suites pass under tasks-axi 0.2.6; environment-only failure

* no-mistakes(document): purge stale fm/ branch naming from docs and headers

* fix(test): assert the merged epoch status wording in the branch-prefix override test

The rebase resolution of tests/fm-brief.test.sh kept the branch's
pre-merge \`done: ready in branch ...\` assertion while the merged
fm-dod-lib.sh (carrying main's epoch-stamped status line) renders
\`done [at=<epoch>]: ready in branch ...\`. Align the assertion so the
override-consistency test matches the behavior it verifies.

* no-mistakes(review): Address remaining branch-prefix findings in four bin scripts

* no-mistakes(test): skip real-tasks-axi tests below the repo's 0.2.6 floor

* no-mistakes(document): document spawn's branch-prefix registry deviation notice
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 26, 2026
…henguid#2648)

* feat(bin): make the ship-branch prefix configurable per project

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.

* no-mistakes(review): Wire branch-prefix intake in AGENTS.md; fix fm-merge-local.sh hardcoded fm/ prefix

* no-mistakes(document): docs: document configurable ship-branch prefix in architecture.md

* no-mistakes(review): Persist immutable branch contracts

* no-mistakes(document): Document configurable ship branch prefixes

* no-mistakes(lint): Captain: fix ShellCheck test warnings

* fix(bin): map bearings PR rows to their recorded ship branch (kunchenguid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.

* fix(ci): align lint arithmetic-looking assignment and stale Bearings snapshot count

- Quote the --branch-prefix want_value assignment in fm-brief.sh, fm-promote.sh,
  and fm-spawn.sh so ShellCheck SC2100 no longer misreads the plain string
  'branch-prefix' as arithmetic shorthand.
- Bump the Stock macOS Bash snapshot job's hardcoded Bearings test-count
  assertion from 59 to 60: this PR added a Bearings test, so the count was
  stale, not the feature.

* no-mistakes(review): fix(bin): honor recorded ship branch in relaunch and review-diff

* no-mistakes(document): docs: complete branch-prefix flag in brief and promote headers

* fix(lint): quote branch-prefix parser token; drop unused BRANCH_Q after rebase

* no-mistakes(review): Restore %q branch escaping in promotion instructions with regression test

* no-mistakes(document): document recorded ship branch and prefix flag

fm-review-diff.sh's header is the owner of its branch-resolution
contract; it still described only the legacy local-branch behavior
after the change made review-diff honor state/<id>.meta's recorded
ship branch. README's feature bullet enumerates the registry's
optional flags and was missing the new branch=<prefix> override.

* no-mistakes(lint): Silence SC2016 on intentional single-quoted sed expression

* no-mistakes(review): address branch-prefix review findings in DoD and project-mode

* no-mistakes(test): branch-prefix suites pass under tasks-axi 0.2.6; environment-only failure

* no-mistakes(document): purge stale fm/ branch naming from docs and headers

* fix(test): assert the merged epoch status wording in the branch-prefix override test

The rebase resolution of tests/fm-brief.test.sh kept the branch's
pre-merge \`done: ready in branch ...\` assertion while the merged
fm-dod-lib.sh (carrying main's epoch-stamped status line) renders
\`done [at=<epoch>]: ready in branch ...\`. Align the assertion so the
override-consistency test matches the behavior it verifies.

* no-mistakes(review): Address remaining branch-prefix findings in four bin scripts

* no-mistakes(test): skip real-tasks-axi tests below the repo's 0.2.6 floor

* no-mistakes(document): document spawn's branch-prefix registry deviation notice
ppetermann added a commit to ppetermann/firstmate that referenced this pull request Sep 26, 2026
…the take-in

This reverts 697d94d (feat: add Devin CLI crewmate and scout adapter, kunchenguid#5380)
and 67130f1 (feat(bin): make the ship-branch prefix configurable per project,
kunchenguid#2648) on top of upstream 9284978, so the P2 take-in merge brings in the rest
of upstream b6930db..9284978 without them.

- Devin: deferred on the captain's word (take-in question 6); it touches the
  composer and control hotspots and nothing here uses it. It can come in whole
  at a later take-in by reverting this revert.
- Ship-branch prefix: skipped for now (report section 2.4); every repository
  this fleet ships to accepts the fm/ prefix.

Resolution: fm-harness.sh keeps ac2ed3b's later secondmate-harness line and
drops devin from ac2ed3b's supervision-branch pin list.
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
…henguid#2648)

* feat(bin): make the ship-branch prefix configurable per project

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.

* no-mistakes(review): Wire branch-prefix intake in AGENTS.md; fix fm-merge-local.sh hardcoded fm/ prefix

* no-mistakes(document): docs: document configurable ship-branch prefix in architecture.md

* no-mistakes(review): Persist immutable branch contracts

* no-mistakes(document): Document configurable ship branch prefixes

* no-mistakes(lint): Captain: fix ShellCheck test warnings

* fix(bin): map bearings PR rows to their recorded ship branch (kunchenguid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.

* fix(ci): align lint arithmetic-looking assignment and stale Bearings snapshot count

- Quote the --branch-prefix want_value assignment in fm-brief.sh, fm-promote.sh,
  and fm-spawn.sh so ShellCheck SC2100 no longer misreads the plain string
  'branch-prefix' as arithmetic shorthand.
- Bump the Stock macOS Bash snapshot job's hardcoded Bearings test-count
  assertion from 59 to 60: this PR added a Bearings test, so the count was
  stale, not the feature.

* no-mistakes(review): fix(bin): honor recorded ship branch in relaunch and review-diff

* no-mistakes(document): docs: complete branch-prefix flag in brief and promote headers

* fix(lint): quote branch-prefix parser token; drop unused BRANCH_Q after rebase

* no-mistakes(review): Restore %q branch escaping in promotion instructions with regression test

* no-mistakes(document): document recorded ship branch and prefix flag

fm-review-diff.sh's header is the owner of its branch-resolution
contract; it still described only the legacy local-branch behavior
after the change made review-diff honor state/<id>.meta's recorded
ship branch. README's feature bullet enumerates the registry's
optional flags and was missing the new branch=<prefix> override.

* no-mistakes(lint): Silence SC2016 on intentional single-quoted sed expression

* no-mistakes(review): address branch-prefix review findings in DoD and project-mode

* no-mistakes(test): branch-prefix suites pass under tasks-axi 0.2.6; environment-only failure

* no-mistakes(document): purge stale fm/ branch naming from docs and headers

* fix(test): assert the merged epoch status wording in the branch-prefix override test

The rebase resolution of tests/fm-brief.test.sh kept the branch's
pre-merge \`done: ready in branch ...\` assertion while the merged
fm-dod-lib.sh (carrying main's epoch-stamped status line) renders
\`done [at=<epoch>]: ready in branch ...\`. Align the assertion so the
override-consistency test matches the behavior it verifies.

* no-mistakes(review): Address remaining branch-prefix findings in four bin scripts

* no-mistakes(test): skip real-tasks-axi tests below the repo's 0.2.6 floor

* no-mistakes(document): document spawn's branch-prefix registry deviation notice
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
…henguid#2648)

* feat(bin): make the ship-branch prefix configurable per project

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.

* no-mistakes(review): Wire branch-prefix intake in AGENTS.md; fix fm-merge-local.sh hardcoded fm/ prefix

* no-mistakes(document): docs: document configurable ship-branch prefix in architecture.md

* no-mistakes(review): Persist immutable branch contracts

* no-mistakes(document): Document configurable ship branch prefixes

* no-mistakes(lint): Captain: fix ShellCheck test warnings

* fix(bin): map bearings PR rows to their recorded ship branch (kunchenguid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.

* fix(ci): align lint arithmetic-looking assignment and stale Bearings snapshot count

- Quote the --branch-prefix want_value assignment in fm-brief.sh, fm-promote.sh,
  and fm-spawn.sh so ShellCheck SC2100 no longer misreads the plain string
  'branch-prefix' as arithmetic shorthand.
- Bump the Stock macOS Bash snapshot job's hardcoded Bearings test-count
  assertion from 59 to 60: this PR added a Bearings test, so the count was
  stale, not the feature.

* no-mistakes(review): fix(bin): honor recorded ship branch in relaunch and review-diff

* no-mistakes(document): docs: complete branch-prefix flag in brief and promote headers

* fix(lint): quote branch-prefix parser token; drop unused BRANCH_Q after rebase

* no-mistakes(review): Restore %q branch escaping in promotion instructions with regression test

* no-mistakes(document): document recorded ship branch and prefix flag

fm-review-diff.sh's header is the owner of its branch-resolution
contract; it still described only the legacy local-branch behavior
after the change made review-diff honor state/<id>.meta's recorded
ship branch. README's feature bullet enumerates the registry's
optional flags and was missing the new branch=<prefix> override.

* no-mistakes(lint): Silence SC2016 on intentional single-quoted sed expression

* no-mistakes(review): address branch-prefix review findings in DoD and project-mode

* no-mistakes(test): branch-prefix suites pass under tasks-axi 0.2.6; environment-only failure

* no-mistakes(document): purge stale fm/ branch naming from docs and headers

* fix(test): assert the merged epoch status wording in the branch-prefix override test

The rebase resolution of tests/fm-brief.test.sh kept the branch's
pre-merge \`done: ready in branch ...\` assertion while the merged
fm-dod-lib.sh (carrying main's epoch-stamped status line) renders
\`done [at=<epoch>]: ready in branch ...\`. Align the assertion so the
override-consistency test matches the behavior it verifies.

* no-mistakes(review): Address remaining branch-prefix findings in four bin scripts

* no-mistakes(test): skip real-tasks-axi tests below the repo's 0.2.6 floor

* no-mistakes(document): document spawn's branch-prefix registry deviation notice
mehulbhagwani pushed a commit to mehulbhagwani/firstmate that referenced this pull request Sep 27, 2026
…henguid#2648)

* feat(bin): make the ship-branch prefix configurable per project

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.

* no-mistakes(review): Wire branch-prefix intake in AGENTS.md; fix fm-merge-local.sh hardcoded fm/ prefix

* no-mistakes(document): docs: document configurable ship-branch prefix in architecture.md

* no-mistakes(review): Persist immutable branch contracts

* no-mistakes(document): Document configurable ship branch prefixes

* no-mistakes(lint): Captain: fix ShellCheck test warnings

* fix(bin): map bearings PR rows to their recorded ship branch (kunchenguid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.

* fix(ci): align lint arithmetic-looking assignment and stale Bearings snapshot count

- Quote the --branch-prefix want_value assignment in fm-brief.sh, fm-promote.sh,
  and fm-spawn.sh so ShellCheck SC2100 no longer misreads the plain string
  'branch-prefix' as arithmetic shorthand.
- Bump the Stock macOS Bash snapshot job's hardcoded Bearings test-count
  assertion from 59 to 60: this PR added a Bearings test, so the count was
  stale, not the feature.

* no-mistakes(review): fix(bin): honor recorded ship branch in relaunch and review-diff

* no-mistakes(document): docs: complete branch-prefix flag in brief and promote headers

* fix(lint): quote branch-prefix parser token; drop unused BRANCH_Q after rebase

* no-mistakes(review): Restore %q branch escaping in promotion instructions with regression test

* no-mistakes(document): document recorded ship branch and prefix flag

fm-review-diff.sh's header is the owner of its branch-resolution
contract; it still described only the legacy local-branch behavior
after the change made review-diff honor state/<id>.meta's recorded
ship branch. README's feature bullet enumerates the registry's
optional flags and was missing the new branch=<prefix> override.

* no-mistakes(lint): Silence SC2016 on intentional single-quoted sed expression

* no-mistakes(review): address branch-prefix review findings in DoD and project-mode

* no-mistakes(test): branch-prefix suites pass under tasks-axi 0.2.6; environment-only failure

* no-mistakes(document): purge stale fm/ branch naming from docs and headers

* fix(test): assert the merged epoch status wording in the branch-prefix override test

The rebase resolution of tests/fm-brief.test.sh kept the branch's
pre-merge \`done: ready in branch ...\` assertion while the merged
fm-dod-lib.sh (carrying main's epoch-stamped status line) renders
\`done [at=<epoch>]: ready in branch ...\`. Align the assertion so the
override-consistency test matches the behavior it verifies.

* no-mistakes(review): Address remaining branch-prefix findings in four bin scripts

* no-mistakes(test): skip real-tasks-axi tests below the repo's 0.2.6 floor

* no-mistakes(document): document spawn's branch-prefix registry deviation notice
@wesleymatosdev

Copy link
Copy Markdown
Contributor Author

Both findings are addressed in the merged code (merge commit 67130f18, verified on main at d5c2507a):

  • Invalid branch names pass validation: the fully composed branch (prefix joined to task id) is validated with git check-ref-format --branch at both generation (bin/fm-brief.sh:285) and merge time (bin/fm-merge-local.sh:99), so an invalid full name is refused with an error rather than reaching git checkout -b.
  • Merge re-resolves mutable branch state: bin/fm-merge-local.sh now reads the intake-time branch= recorded in state/<id>.meta (line 97) and only falls back to fm/<id> when absent — it no longer derives the branch from the live registry.

RooseveltAdvisors pushed a commit to RooseveltAdvisors/firstmate that referenced this pull request Sep 29, 2026
…henguid#2648)

* feat(bin): make the ship-branch prefix configurable per project

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.

* no-mistakes(review): Wire branch-prefix intake in AGENTS.md; fix fm-merge-local.sh hardcoded fm/ prefix

* no-mistakes(document): docs: document configurable ship-branch prefix in architecture.md

* no-mistakes(review): Persist immutable branch contracts

* no-mistakes(document): Document configurable ship branch prefixes

* no-mistakes(lint): Captain: fix ShellCheck test warnings

* fix(bin): map bearings PR rows to their recorded ship branch (kunchenguid#1887)

fm-bearings-snapshot.sh keyed a PR back to its task by string-matching
the headRefName against the fm/ prefix, so any project whose branch
prefix was overridden (e.g. via kunchenguid#2648's branch=<prefix> registry
annotation) had its PRs silently drop to task "-" in the bearings
view, exactly the third fm/-assumption issue kunchenguid#1887 named alongside
fm-merge-local.sh and fm-bearings-snapshot.sh itself.

fm-fleet-snapshot.sh now surfaces each task's recorded branch=
metadata field in its JSON task rows, and fm-bearings-snapshot.sh
cross-references a PR's headRefName against those recorded branches
before falling back to the legacy fm/ prefix heuristic, so a custom
branch prefix maps a PR back to its real task.

Adds a regression test proving a PR opened against a fix/<task-id>
branch resolves to that task instead of "-"; confirmed it fails on
the prior startswith("fm/") logic and passes with this change.
ShellCheck clean; full fm-bearings-snapshot.test.sh and
fm-fleet-snapshot-view.test.sh suites pass.

* fix(ci): align lint arithmetic-looking assignment and stale Bearings snapshot count

- Quote the --branch-prefix want_value assignment in fm-brief.sh, fm-promote.sh,
  and fm-spawn.sh so ShellCheck SC2100 no longer misreads the plain string
  'branch-prefix' as arithmetic shorthand.
- Bump the Stock macOS Bash snapshot job's hardcoded Bearings test-count
  assertion from 59 to 60: this PR added a Bearings test, so the count was
  stale, not the feature.

* no-mistakes(review): fix(bin): honor recorded ship branch in relaunch and review-diff

* no-mistakes(document): docs: complete branch-prefix flag in brief and promote headers

* fix(lint): quote branch-prefix parser token; drop unused BRANCH_Q after rebase

* no-mistakes(review): Restore %q branch escaping in promotion instructions with regression test

* no-mistakes(document): document recorded ship branch and prefix flag

fm-review-diff.sh's header is the owner of its branch-resolution
contract; it still described only the legacy local-branch behavior
after the change made review-diff honor state/<id>.meta's recorded
ship branch. README's feature bullet enumerates the registry's
optional flags and was missing the new branch=<prefix> override.

* no-mistakes(lint): Silence SC2016 on intentional single-quoted sed expression

* no-mistakes(review): address branch-prefix review findings in DoD and project-mode

* no-mistakes(test): branch-prefix suites pass under tasks-axi 0.2.6; environment-only failure

* no-mistakes(document): purge stale fm/ branch naming from docs and headers

* fix(test): assert the merged epoch status wording in the branch-prefix override test

The rebase resolution of tests/fm-brief.test.sh kept the branch's
pre-merge \`done: ready in branch ...\` assertion while the merged
fm-dod-lib.sh (carrying main's epoch-stamped status line) renders
\`done [at=<epoch>]: ready in branch ...\`. Align the assertion so the
override-consistency test matches the behavior it verifies.

* no-mistakes(review): Address remaining branch-prefix findings in four bin scripts

* no-mistakes(test): skip real-tasks-axi tests below the repo's 0.2.6 floor

* no-mistakes(document): document spawn's branch-prefix registry deviation notice
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.

3 participants