Skip to content

fix(bin): make a task's git branch prefix configurable per project - #4384

Closed
JoeyRichter wants to merge 1 commit into
kunchenguid:mainfrom
JoeyRichter:fm/fm-ccrm-branch-naming
Closed

JoeyRichter wants to merge 1 commit into
kunchenguid:mainfrom
JoeyRichter:fm/fm-ccrm-branch-naming

Conversation

@JoeyRichter

@JoeyRichter JoeyRichter commented Sep 13, 2026 •

Copy link
Copy Markdown

Intent

Firstmate named every task branch fm/<task-id> in several places that each re-derived the pattern. A project whose repository enforces its own branch-naming convention (for example a Conventional-Commits-style check that only exempts prefixes such as chore/, docs/, ci/, build/) had no way to ship a compliant branch, so every firstmate-opened non-ticket PR there tripped that check.

Change

  • New bin/fm-branch-lib.sh is the single owner of a task's branch name: a per-project prefix read from the data/projects.md registry's optional branch=<prefix> token (default fm/, so unconfigured projects are unchanged), plus the reverse PR-head-to-task-id mapping the bearings board needs.
  • A shared fm_registry_posture_tokens helper tokenizes the posture bracket once for both fm-branch-lib.sh and fm-project-mode.sh. A configured prefix of an unsupported shape fails closed.
  • Every consumer resolves the branch name through the helper: fm-brief.sh, fm-dod-lib.sh, fm-merge-local.sh, fm-review-diff.sh, fm-promote.sh, and fm-bearings-snapshot.sh.
  • The bearings PR-head link is gated on a known task id for the configured-prefix shape only, so a repository's own human branches are never claimed as tasks, while the default fm/ shape keeps its prior behavior.
  • Tests cover the forward map, the reverse map (including ids that begin with fm-), name-or-path input, and fail-closed handling; registry-format docs note the new token.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

First look on main b182d0f908b78d08c7ccb8dce3775bdca8c5d657. Tip fd378e4aecaa34cc21fcfa3c57a5e4d6d73b7713. Fork PR (JoeyRichter).

What I inspected (diff vs main)

  • New bin/fm-branch-lib.sh single-owns branch naming: default fm/; optional registry branch=<prefix> validated to fm/ or <segment>/fm- (rejects fm/fm-, multi-segment, empty).
  • Rewires brief/dod/merge-local/promote/review-diff + shared posture tokenizer in project-mode; bearings reverse-maps with known-id gate only on non-default shape.
  • Docs + tests. No .github/workflows delta. No credential paths, no eval/RCE surface; prefix is registry-captain-controlled and fail-closed on bad shape.
  • Unconfigured project (no branch=) stays fm/<id> — confirmed by lib default + tests in the diff.

contract-class: opt-in — default fm/ preserved without branch=.

Attestation: MATCH (body head_sha == tip).

Checks: first-time fork workflows approved this pass — CI 34774313625 (now queued), Require no-mistakes 34774313536. Waiting-ci for green tip CI+NM.

VISION.md (each rule)

  • One captain, one interface: aligns — naming stays below deck; captain still talks outcomes.
  • Authority is explicit: aligns as opt-in — branch= only when registered; no silent default change.
  • Scripts own the mechanics: aligns — single-owner lib + tokenizer.
  • A restart is a non-event: aligns — registry-driven; no ephemeral-only naming.
  • Delegation with a spine: aligns — brief/DoD/merge/promote share one name.
  • The fleet outlives any vendor: aligns — forge-agnostic branch strings.
  • Scope: aligns — command-layer branch contract.

Security FYI (tip only, not waiting-captain): none material — no workflows, no secrets, validated prefix shapes. Surface overlaps open #2648 (same configurable-prefix theme); this tip is the JoeyRichter fork attestation MATCH path.

Merge-eligible N. Waiting-ci. Do not merge. No Firstmate flag (not otherwise-ready / not a security|default-behavior gate).

Firstmate named every task branch fm/<id> in six places that each re-derived
the pattern, so a project whose repository enforces its own branch-naming
convention (e.g. a Conventional-Commits check that only exempts prefixes such
as chore/, docs/, ci/, build/) had no way to ship a compliant branch, and every
firstmate-opened non-ticket PR there tripped that check.

Add bin/fm-branch-lib.sh as the single owner of a task's branch name: a
per-project prefix read from the data/projects.md registry's optional
branch=<prefix> token (default fm/, so unconfigured projects are unchanged),
and the reverse PR-head-to-task-id mapping the bearings board needs. The reverse
map is prefix-shape based and leftmost, so a task id that itself begins with fm-
is never over-stripped. A shared fm_registry_posture_tokens helper tokenizes the
posture bracket once for both fm-branch-lib.sh and fm-project-mode.sh, and a
configured prefix of an unsupported shape (including one whose leading segment is
fm) fails closed.

Route every consumer through it: fm-brief.sh, fm-dod-lib.sh, fm-merge-local.sh,
fm-review-diff.sh, fm-promote.sh, and fm-bearings-snapshot.sh. The bearings
PR-head link is gated on a known task id for the configured-prefix shape only,
so a repository's own human branches are never claimed as tasks while the
default fm/ shape keeps its prior behavior. Registry-format docs note the new
token, and the stock-Bash CI bearings-count assertion is updated for the added
test.
@JoeyRichter
JoeyRichter force-pushed the fm/fm-ccrm-branch-naming branch from d106f18 to e6aa3ce Compare September 13, 2026 19:22
@JoeyRichter
JoeyRichter deleted the fm/fm-ccrm-branch-naming branch September 13, 2026 19:27
@JoeyRichter
JoeyRichter restored the fm/fm-ccrm-branch-naming branch September 13, 2026 19:34
@JoeyRichter JoeyRichter reopened this Sep 13, 2026
@JoeyRichter

Copy link
Copy Markdown
Author

Note: this PR was briefly closed and reopened while fixing up the commit history (rewritten for correct authorship and to remove some leftover data that shouldn't have been in a test fixture). A separate attempt to also rename the branch turned out to close cross-fork PRs irreversibly on GitHub, so that was reverted and the original branch name restored. No functional changes to the actual diff.

@JoeyRichter

Copy link
Copy Markdown
Author

Closing without merging: PR #2648 already covers this exact ask (per-project branch= opt-in in data/projects.md, default fm/ unchanged, resolved through the brief/spawn/merge-local/promote/review-diff/bearings chain) and is further along with existing maintainer engagement on the tracking issue (#1887). Rather than maintain a competing duplicate implementation, deferring to #2648 - happy to help review it if useful.

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.

2 participants