Skip to content

fix(bin): print git switch instead of git checkout for branch commands - #3048

Open
joaodiniz99 wants to merge 2 commits into
kunchenguid:mainfrom
joaodiniz99:fm/brief-branch-cmd
Open

fix(bin): print git switch instead of git checkout for branch commands#3048
joaodiniz99 wants to merge 2 commits into
kunchenguid:mainfrom
joaodiniz99:fm/brief-branch-cmd

Conversation

@joaodiniz99

Copy link
Copy Markdown

Intent

Replace every INSTRUCTION this repository prints that tells an agent to run 'git checkout' with the equivalent 'git switch' form. Scope: three confirmed printed-instruction sites - bin/fm-brief.sh's generated ship-brief setup step ('git checkout -b fm/$ID' -> 'git switch -c fm/$ID'), bin/fm-guard.sh's tangle remediation printf, and bin/fm-bootstrap.sh's TANGLE diagnostic remediation echo - plus every tracked prose site that quotes those printed commands (found: .agents/skills/bootstrap-diagnostics/SKILL.md) and every test assertion matching the old printed strings (found and updated: tests/fm-tangle-guard.test.sh, tests/fm-session-start.test.sh). Reason for the change, for the PR description: git permission policies commonly deny git checkout outright because its file-restoring forms discard uncommitted work, which would block the very first instruction this repo prints to an agent (git checkout -b) and stop all work; git switch -c / git switch are the purpose-built modern equivalents (Git 2.23+, 2019) and are strictly safer since they refuse rather than discard conflicting local modifications, with destructive behavior gated behind explicit --force/--discard-changes flags a permission policy can deny on its own - so this is the objectively correct command for what these lines do, not a workaround. Explicitly OUT of scope, left untouched on purpose: git checkout calls a script EXECUTES itself (never printed as an instruction to an agent) in bin/fm-teardown.sh:2682 and :2695 and bin/fm-remote-secondmate-control.sh:277, since those run inside a script process and are never subject to an agent permission layer; bin/fm-tangle-lib.sh's comment, which uses 'checkout' as a noun, not a command; and every 'git checkout' invocation inside tests/ that is test-fixture setup executing the command itself rather than asserting on printed output (e.g. tests/fm-bearings-snapshot.test.sh, tests/fm-fleet-sync.test.sh, tests/fm-crew-state.test.sh, tests/fm-tangle-guard.test.sh's own repo-state helpers). No renaming, no restructuring, no unrelated fixes - diff is exactly these 6 files, 12 changed lines. Verification already performed by me: ran tests/fm-tangle-guard.test.sh, tests/fm-session-start.test.sh, tests/fm-bootstrap.test.sh, and tests/fm-brief.test.sh individually and via bin/fm-test-run.sh (all pass, 0 failures); ran bin/fm-lint.sh with pinned ShellCheck 0.11.0 and actionlint 1.7.12 installed locally (clean, exit 0); ran bin/fm-doc-audience-check.sh (ok). This PR targets a fork (push remote https://github.com/joaodiniz99/firstmate.git, PR opens against upstream kunchenguid/firstmate) - the PR description must stand alone for a maintainer with none of this context, state the defect and the general git-switch-is-correct/safer argument, and must NOT mention any individual machine's permission configuration as the motivation.

What Changed

  • The three sites that print a branch command for an agent to run now emit the git switch form: the generated ship brief's first setup step (git switch -c fm/$ID in bin/fm-brief.sh), the tangle remediation banner in bin/fm-guard.sh, and the TANGLE: diagnostic remediation in bin/fm-bootstrap.sh (git -C <root> switch <default>).
  • Prose that quotes those printed commands was aligned: .agents/skills/bootstrap-diagnostics/SKILL.md now quotes git -C <root> switch <default>, and docs/configuration.md describes the omitted remediation as the "branch-restore command" rather than "the checkout command" (same wording fix in the bin/fm-bootstrap.sh header comment).
  • Test assertions matching the old printed strings were updated in tests/fm-tangle-guard.test.sh and tests/fm-session-start.test.sh, including the brief guard that locates the branch step by git switch -c fm/. git checkout calls that a script executes itself, and test-fixture setup that runs the command directly, are unchanged: only printed instructions moved.

Risk Assessment

✅ Low: A mechanical, semantically equivalent 12-line substitution of git checkout with the purpose-built git switch form in three printed-instruction sites plus their prose and test assertions, with no consumer coupled to the old strings and no behavioral difference reachable given how the default branch name is resolved.

Testing

Ran the four targeted suites named in the intent (fm-tangle-guard, fm-session-start, fm-brief, fm-bootstrap) individually — all pass, zero failures — and then produced product-level evidence rather than relying on those assertions: I generated a real ship brief and executed its printed first action (git switch -c fm/switch-demo-e2e) verbatim inside a detached-HEAD worktree, and I stranded a temp primary on a feature branch to trigger both the fm-guard banner and the fm-bootstrap TANGLE diagnostic, then ran the printed git -C &lt;primary&gt; switch main and confirmed the primary was restored with the feature branch intact. A before/after run of the base-commit scripts against identical inputs shows all three sites moving from checkout to switch, and a small git demo backs the PR's safety argument by showing git checkout -- &lt;file&gt; silently discards uncommitted work while git switch has no such form. This change has no UI, HTML, or rendered surface — the end-user surface is CLI and generated-Markdown text, so the evidence is captured as command transcripts and the generated brief section. I also confirmed the intent's out-of-scope claim holds: every remaining git checkout in bin/ and tests/ is a command a script executes itself or test-fixture setup, never an instruction printed to an agent. The worktree is clean; all scratch repos were created under /tmp.

Evidence: Generated ship brief: the Setup section a crewmate agent reads

Source: Generated ship brief: the Setup section a crewmate agent reads

# Setup You are in a disposable git worktree of alpha, at a detached HEAD on a clean default branch. Verify isolation before anything else. Run pwd -P and git rev-parse --show-toplevel; both must resolve to the disposable task worktree you were launched in... 1. First action: create your branch: git switch -c fm/switch-demo-e2e 2. Run no-mistakes doctor; if it reports the repo is not initialized here, run no-mistakes init.

=== 1. Generate a REAL ship brief with bin/fm-brief.sh ===
brief: /tmp/fm-switch-evidence.DnNhpR/home/data/switch-demo-e2e/brief.md

--- the Setup section the crewmate agent actually reads ---
# Setup
You are in a disposable git worktree of alpha, at a detached HEAD on a clean default branch.

**Verify isolation before anything else.** Run `pwd -P` and `git rev-parse --show-toplevel`; both must resolve to the disposable task worktree you were launched in, such as a treehouse pool path or an Orca-managed worktree, not the primary checkout firstmate operates from.
The path check is authoritative: `git rev-parse --git-dir` and `git rev-parse --git-common-dir` can help inspect the repo, but they do not prove you are outside the primary checkout.
If the top-level path is the primary checkout or not the worktree you were launched in, STOP - do not branch or commit here - append `blocked: launched in primary checkout, not an isolated worktree` to the status file and stop.

1. First action: create your branch: `git switch -c fm/switch-demo-e2e`
2. Run `no-mistakes doctor`; if it reports the repo is not initialized here, run `no-mistakes init`.

# Rules
Evidence: Agent follows the printed instruction verbatim (end-to-end)

Source: Agent follows the printed instruction verbatim (end-to-end)

$ disposable worktree state before the first action: ## HEAD (no branch) $ # command copied verbatim out of the generated brief $ git switch -c fm/switch-demo-e2e Switched to a new branch 'fm/switch-demo-e2e' $ git -C <worktree> status -sb ## fm/switch-demo-e2e RESULT: the first instruction the repo prints to an agent executes cleanly and creates the task branch.

=== 2. An agent follows that printed instruction verbatim, in the state the brief describes ===
$ disposable worktree state before the first action:
## HEAD (no branch)

$ # command copied verbatim out of the generated brief
$ git switch -c fm/switch-demo-e2e
Switched to a new branch 'fm/switch-demo-e2e'

$ git -C <worktree> status -sb
## fm/switch-demo-e2e
$ git -C <worktree> rev-parse --abbrev-ref HEAD
fm/switch-demo-e2e

RESULT: the first instruction the repo prints to an agent executes cleanly and creates the task branch.
Evidence: Tangle remediation: fm-guard banner + fm-bootstrap diagnostic, then the printed command run

Source: Tangle remediation: fm-guard banner + fm-bootstrap diagnostic, then the printed command run

$ bin/fm-guard.sh # what the captain sees ● WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH ● <primary> is on 'fm/readme-restructure-d3', not its default branch 'main'. ● The work is SAFE on the 'fm/readme-restructure-d3' ref. ● Restore the primary to 'main': ● git -C <primary> switch main ● then re-validate 'fm/readme-restructure-d3' in a proper isolated worktree. $ bin/fm-bootstrap.sh # the TANGLE diagnostic line TANGLE: primary checkout on feature branch 'fm/readme-restructure-d3' (expected 'main'); the work is safe on that ref - restore the primary with: git -C <primary> switch main, then re-validate the branch in a proper worktree $ # captain copies the printed restore command verbatim and runs it $ git -C <primary> switch main Switched to branch 'main' $ git -C <primary> branch --list fm/readme-restructure-d3 # the work is still there fm/readme-restructure-d3

=== 3. Tangle remediation: fm-guard.sh banner + fm-bootstrap.sh diagnostic ===
$ # primary checkout stranded on a feature branch (the 'tangle')
fm/readme-restructure-d3

$ bin/fm-guard.sh   # what the captain sees
●  WORKTREE TANGLE - PRIMARY CHECKOUT IS ON A FEATURE BRANCH
●  /tmp/fm-switch-evidence.DnNhpR/primary is on 'fm/readme-restructure-d3', not its default branch 'main'.
●  A crewmate likely branched/committed in the primary instead of its own worktree.
●  The work is SAFE on the 'fm/readme-restructure-d3' ref.
●  Restore the primary to 'main':
●      git -C /tmp/fm-switch-evidence.DnNhpR/primary switch main
●  then re-validate 'fm/readme-restructure-d3' in a proper isolated worktree.
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

$ bin/fm-bootstrap.sh   # the TANGLE diagnostic line
TANGLE: primary checkout on feature branch 'fm/readme-restructure-d3' (expected 'main'); the work is safe on that ref - restore the primary with: git -C /tmp/fm-switch-evidence.DnNhpR/primary switch main, then re-validate the branch in a proper worktree

$ # captain copies the printed restore command verbatim and runs it
$ git -C /tmp/fm-switch-evidence.DnNhpR/primary switch main
Switched to branch 'main'
$ git -C <primary> rev-parse --abbrev-ref HEAD
main
$ git -C <primary> branch --list fm/readme-restructure-d3   # the work is still there, nothing stranded
  fm/readme-restructure-d3

RESULT: both remediation surfaces print 'git switch', and the printed command restores the primary.
Evidence: Before/after on identical inputs (base 6a2cd6c vs this change)

Source: Before/after on identical inputs (base 6a2cd6c vs this change)

[BEFORE] ship brief, step 1: 1. First action: create your branch: git checkout -b fm/ba-demo-77 [BEFORE] fm-guard.sh tangle banner: ● git -C <primary> checkout main [BEFORE] fm-bootstrap.sh TANGLE diagnostic: git -C <primary> checkout main [AFTER] ship brief, step 1: 1. First action: create your branch: git switch -c fm/ba-demo-77 [AFTER] fm-guard.sh tangle banner: ● git -C <primary> switch main [AFTER] fm-bootstrap.sh TANGLE diagnostic: git -C <primary> switch main

=== 5. Before / after, same inputs (base 6a2cd6c vs this change) ===

[BEFORE] ship brief, step 1 (what the crewmate agent is told to run first):
      1. First action: create your branch: `git checkout -b fm/ba-demo-77`
[BEFORE] fm-guard.sh tangle banner, restore command:
      ●      git -C <primary> checkout main
[BEFORE] fm-bootstrap.sh TANGLE diagnostic, restore command:
      git -C <primary> checkout main

[AFTER] ship brief, step 1 (what the crewmate agent is told to run first):
      1. First action: create your branch: `git switch -c fm/ba-demo-77`
[AFTER] fm-guard.sh tangle banner, restore command:
      ●      git -C <primary> switch main
[AFTER] fm-bootstrap.sh TANGLE diagnostic, restore command:
      git -C <primary> switch main

RESULT: all three printed-instruction sites changed from 'git checkout' to 'git switch'.
Evidence: Why git switch is the correct command, demonstrated

Source: Why git switch is the correct command, demonstrated

git version 2.50.1 $ git -C <repo> checkout -- f.txt # the file-restoring form: silently destroys it exit=0 f.txt is now: committed <-- work gone, no prompt, no confirmation $ git -C <repo> switch -- f.txt # git switch has no such form at all fatal: invalid reference: f.txt exit=128 f.txt is still: UNCOMMITTED WORK <-- work intact

=== 4. Why 'git switch' is the correct command, not a workaround ===
git version: git version 2.50.1 (Apple Git-155)

$ # uncommitted work in the tree:
 M f.txt
UNCOMMITTED WORK

$ git -C <repo> checkout -- f.txt      # the file-restoring form: silently destroys it
  exit=0
  f.txt is now: committed   <-- work gone, no prompt, no confirmation

$ git -C <repo> switch -- f.txt       # git switch has no such form at all
fatal: invalid reference: f.txt
  exit=128
  f.txt is still: UNCOMMITTED WORK   <-- work intact

RESULT: 'git checkout' is one CLI verb covering both branch switching and destructive file
restore, so a policy can only allow or deny the whole verb. 'git switch' does only the branch
operation; its destructive behavior sits behind explicit --force/--discard-changes flags.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ℹ️ docs/configuration.md:338 - docs/configuration.md:338-339 still describe the TANGLE remediation as "the printed checkout remediation" and "omits the checkout command", but the printed command is now git -C &lt;root&gt; switch &lt;default&gt;. These lines describe the command rather than quoting it, so they fall outside the intent's stated prose scope ("every tracked prose site that quotes those printed commands") and the intent explicitly caps the diff at these 6 files. Noting only; no change recommended in this change.
✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-tangle-guard.test.sh (6 assertions, exit 0) - covers the guard banner, bootstrap TANGLE line, and brief branch-step ordering
  • bash tests/fm-session-start.test.sh (49 assertions, exit 0) - covers the read-only bootstrap and --reemit restore-remediation assertions
  • bash tests/fm-brief.test.sh (exit 0) - full ship-brief scaffolding suite
  • bash tests/fm-bootstrap.test.sh (28 assertions, exit 0)
  • Manual E2E: generated a real brief via FM_HOME=&lt;tmp&gt; bin/fm-brief.sh switch-demo-e2e alpha --mode no-mistakes, then ran its printed step 1 (git switch -c fm/switch-demo-e2e) verbatim inside a detached-HEAD linked worktree and confirmed the branch was created
  • Manual E2E: stranded a temp primary on fm/readme-restructure-d3, ran FM_ROOT_OVERRIDE=&lt;repo&gt; bin/fm-guard.sh and bin/fm-bootstrap.sh, then executed the printed git -C &lt;primary&gt; switch main and confirmed the primary was restored with the feature branch intact
  • Manual before/after: ran base-commit (6a2cd6c) copies of fm-brief.sh, fm-guard.sh and fm-bootstrap.sh against identical inputs and diffed the printed commands against the current versions
  • Manual rationale check: demonstrated that git checkout -- &lt;file&gt; silently discards uncommitted work while git switch -- &lt;file&gt; refuses (exit 128, file intact)
  • Scope sweep: grep -rn &#34;git checkout&#34; over bin/, .agents/, docs and tests to confirm every remaining occurrence is script-executed or test-fixture setup rather than a printed instruction
⚠️ **Document** - 1 info
  • ℹ️ docs/configuration.md:338 - Judgment call, left intentionally unchanged: docs/configuration.md:338 ("follow the printed checkout remediation"), AGENTS.md:168 and :176 ("without a checkout repair command"), and docs/configuration.md:655 ("suppress ... checkout repair commands") all use "checkout" as a noun meaning the primary working copy, matching bin/fm-tangle-lib.sh's comment that the change deliberately left alone. They remain accurate and were not reworded. Only the two bare "the checkout command" phrasings, which read as naming the git checkout command itself, were corrected.
⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 1)

🔧 Fix: no lint fixes needed; linter clean with pinned tools
1 warning still open:

  • ⚠️ linter found issues (exit code 1)
✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge with no actionable defects identified.

The changed commands are semantically appropriate for branch creation and switching, output documentation and tests remain aligned, and no reachable regression or repository-rule conflict was established.

Reviews (1): Last reviewed commit: "no-mistakes(document): align tangle docs..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant