Skip to content

feat(bin): detect diverged primary default branch at session start - #5

Merged
wongsuwarn merged 7 commits into
mainfrom
fm/firstmate-session-start-divergence-check
Aug 5, 2026
Merged

wongsuwarn merged 7 commits into
mainfrom
fm/firstmate-session-start-divergence-check

Conversation

@wongsuwarn

@wongsuwarn wongsuwarn commented Aug 5, 2026 •

Copy link
Copy Markdown
Owner

Intent

Follow-up to the already-shipped MAIN_DIVERGED session-start check (PR #5, already merged/CI-green for the core feature): give MAIN_DIVERGED the same FM_BOOTSTRAP_DETECT_ONLY read-only branching that the neighboring TANGLE check already has. Motivation: an earlier review round approved adding a 'git -C fetch origin' instruction to MAIN_DIVERGED's remediation text (since the underlying check deliberately never fetches, for cheapness/offline-safety, so its comparison can be stale). A later review round correctly flagged that this fetch instruction is a write to the primary's object store/refs, and printing it unconditionally in a detect-only/lock-refused session (a read-only session that did not acquire the fleet lock, per AGENTS.md section 3) is inconsistent with TANGLE's own established convention of dropping its printed repair command in that same read-only mode. This change: in bin/fm-bootstrap.sh, MAIN_DIVERGED now branches on FM_BOOTSTRAP_DETECT_ONLY exactly like TANGLE - printing advisory-only wording with no fetch instruction when detect-only/lock-refused, and the full fetch-then-reconcile remediation otherwise. The check function itself (fm_primary_diverged_branch in bin/fm-main-divergence-lib.sh) is completely unchanged and stays fetch-free in every mode - only the printed human-facing text branches. Updated the fm-bootstrap.sh header note, the bootstrap-diagnostics skill's MAIN_DIVERGED bullet, and docs/architecture.md + docs/configuration.md (which both previously claimed the wording was 'identical' between locked and read-only sessions, no longer true) to describe the split, mirroring how those same docs already describe TANGLE's own split. Added a test case to tests/fm-main-divergence.test.sh asserting the detect-only line still names the diverged branch and fires the alarm but omits 'fetch origin', while normal mode includes it. This is purely a wording/behavior split in the diagnostic output; no change to detection logic, no change to what triggers the alarm, and no fetch or mutation added anywhere.

What Changed

  • Added a session-start check that detects when the primary checkout's default branch (main) has diverged from origin, printing a MAIN_DIVERGED: diagnostic; the check function (fm_primary_diverged_branch in bin/fm-main-divergence-lib.sh) stays fetch-free in every mode, and the human-facing text branches on FM_BOOTSTRAP_DETECT_ONLY — advisory-only wording with no git fetch origin instruction in detect-only/lock-refused read-only sessions, and the full fetch-then-reconcile remediation otherwise, mirroring the neighboring TANGLE split.
  • Hardened teardown and wake handling across bin/: abort parked runs and reap leaked processes before teardown, persist secondmate parent bindings for cleanup, surface fleet-wide open decisions on every wake drain, bound remote SSH dead-peer detection, and resolve fm-remote-entrypoint.sh SCRIPT_DIR through a PATH symlink.
  • Enforced latest AXI-family tool floors during bootstrap, enabled herdr presentation spaces by default, gated Calm built-in overrides by activation state, and updated the bootstrap-diagnostics skill plus docs/architecture.md and docs/configuration.md to describe the new detect-only/locked wording split.

Risk Assessment

✅ Low: A text-only diagnostic-wording split that faithfully mirrors the established TANGLE detect-only convention, leaves detection logic and the fetch-free check function untouched, and is covered by an updated test and consistent docs.

Testing

Ran the smallest relevant suite (tests/fm-main-divergence.test.sh, exit 0) which now asserts the detect-only MAIN_DIVERGED line still names the diverged branch and fires the alarm while omitting the fetch origin instruction, and that normal mode includes it. To show the actual end-user experience, I reproduced both modes by driving bin/fm-bootstrap.sh over a hermetic temp git repo with a local-only diverged default branch and captured the rendered diagnostic text as a CLI transcript: the fleet-lock (normal) line prints the full fetch-then-reconcile remediation with git -C <root> fetch origin, and the read-only detect-only line prints advisory-only wording ("read-only session must leave refreshing and reconciling to the session holding the fleet lock") with no fetch or mutation. The check library is untouched by the commit. Everything passed; no findings.

Evidence: MAIN_DIVERGED diagnostic in normal vs detect-only mode (CLI transcript)

=== NORMAL (fleet-lock holder) === MAIN_DIVERGED: primary checkout's 'main' carries commits origin/main does not; ... refresh the stale ref first with: git -C <root> fetch origin ... inspect with: git -C <root> log origin/main..main --oneline, then reconcile manually ... before rerunning /updatefirstmate === DETECT-ONLY (read-only, lock-refused) === MAIN_DIVERGED: primary checkout's 'main' carries commits origin/main did not have as of the last refresh; fast-forward self-update may no longer be able to reconcile it - this check never fetches, so the comparison may be stale; read-only session must leave refreshing and reconciling to the session holding the fleet lock

=== NORMAL (fleet-lock holder) session-start MAIN_DIVERGED line ===
MAIN_DIVERGED: primary checkout's 'main' carries commits origin/main does not; fast-forward 
self-update can no longer reconcile it - this check never fetches, so refresh the stale ref first 
with: git -C /var/folders/rh/yqwk0d3917v6g12q93714vk00000gn/T/tmp.yKtugNIT1C/tracked fetch origin, 
which alone can clear it; if it persists, inspect with: git -C 
/var/folders/rh/yqwk0d3917v6g12q93714vk00000gn/T/tmp.yKtugNIT1C/tracked log origin/main..main 
--oneline, then reconcile manually (rebase/merge onto origin/main, or push the local commits) 
before rerunning /updatefirstmate

=== DETECT-ONLY (read-only, lock-refused) session-start MAIN_DIVERGED line ===
MAIN_DIVERGED: primary checkout's 'main' carries commits origin/main did not have as of the last 
refresh; fast-forward self-update may no longer be able to reconcile it - this check never fetches, 
so the comparison may be stale; read-only session must leave refreshing and reconciling to the 
session holding the fleet lock

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-main-divergence.test.sh — all 3 cases pass, including the new detect-only assertions (line names main, fires alarm, omits fetch origin)
  • Manual CLI reproduction: ran bin/fm-bootstrap.sh against a hermetic temp repo with a local-only diverged main in both normal and FM_BOOTSTRAP_DETECT_ONLY=1 modes, capturing the actual session-start MAIN_DIVERGED: diagnostic text a captain would read
  • Verified target commit c29e532 does not modify bin/fm-main-divergence-lib.sh (the fm_primary_diverged_branch check function), confirming detection logic and fetch-free behavior are unchanged
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@wongsuwarn
wongsuwarn force-pushed the fm/firstmate-session-start-divergence-check branch from 135195b to c29e532 Compare August 5, 2026 17:36
Add a cheap, detect-only bootstrap check that flags when the primary
checkout's local default branch carries commits origin/<default> does
not, so the fast-forward-only self-update path can no longer reconcile
it and would otherwise silently skip with no alarm.
The MAIN_DIVERGED remediation instructs a git fetch on the primary, so
a detect-only, lock-refused session must not print it, matching how
TANGLE already drops its own state-suggesting command there.
@wongsuwarn
wongsuwarn force-pushed the fm/firstmate-session-start-divergence-check branch from c29e532 to 446f382 Compare August 5, 2026 21:19
@wongsuwarn
wongsuwarn merged commit 2565b0c into main Aug 5, 2026
13 checks passed
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