Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/bootstrap-diagnostics/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ When any diagnostic needs captain attention, report the plain consequence and re
Rerun the printed command; it is idempotent and re-derives every finding.
A `hit the ...s bound` line means one of those checks is slow or unreachable - most often a remote secondmate host - and the stage stopped rather than letting it wedge; a `lock was no longer held` line means the session that asked for the sweeps no longer owns them, so leave them to the session that does.
- `TANGLE: <remediation>` - the primary checkout is stranded on a feature branch instead of its default branch; `AGENTS.md` section 8 explains why this guard exists and what it protects.
The work is safe on that branch ref; restore the primary to its default branch with the printed `git -C <root> checkout <default>`, then re-validate that branch in a proper worktree.
The work is safe on that branch ref; restore the primary to its default branch with the printed `git -C <root> switch <default>`, then re-validate that branch in a proper worktree.
This is the only sanctioned firstmate-initiated git write to the primary, and it is a non-destructive branch switch that strands nothing.
- `STARTUP_MEMORY_BUDGET: invalid config/startup-memory-budget - <reason>` - the visible startup-memory budget is not a safe one-line positive decimal file; do not infer the default or propagate it.
Correct the local primary file, then rerun session start so the normal convergence path can deliver the validated value to secondmate homes.
Expand Down
4 changes: 2 additions & 2 deletions bin/fm-bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
# secondmate_handoff_resume, x_mode_setup, fleet_sync) while still
# printing every read-only detect line
# above; the TANGLE line switches to advisory-only wording with no
# checkout command. Used by
# branch-restore command. Used by
# fm-session-start.sh's read-only path when another live session holds
# the fleet lock, so a second concurrent session never race-mutates
# PR-check artifacts, secondmate homes, pending handoff outboxes,
Expand Down Expand Up @@ -1250,7 +1250,7 @@ detect_local_config() {
if [ "${FM_BOOTSTRAP_DETECT_ONLY:-0}" = 1 ] && [ "${FM_BOOTSTRAP_LOCKED:-0}" != 1 ]; then
echo "TANGLE: primary checkout on feature branch '$tangle_branch' (expected '$tangle_default'); the work is safe on that ref - read-only session must leave restore work to the session holding the fleet lock"
else
echo "TANGLE: primary checkout on feature branch '$tangle_branch' (expected '$tangle_default'); the work is safe on that ref - restore the primary with: git -C $FM_ROOT checkout $tangle_default, then re-validate the branch in a proper worktree"
echo "TANGLE: primary checkout on feature branch '$tangle_branch' (expected '$tangle_default'); the work is safe on that ref - restore the primary with: git -C $FM_ROOT switch $tangle_default, then re-validate the branch in a proper worktree"
fi
fi
crew=
Expand Down
2 changes: 1 addition & 1 deletion bin/fm-brief.sh
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ You are in a disposable git worktree of $REPO, at a detached HEAD on a clean def
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 checkout -b fm/$ID\`$SETUP2
1. First action: create your branch: \`git switch -c fm/$ID\`$SETUP2

# Rules
$RULE1
Expand Down
2 changes: 1 addition & 1 deletion bin/fm-guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ if [ -n "$tangle_branch" ]; then
printf '● This read-only session must leave restore work to a session with verified fleet-lock ownership.\n'
else
printf "● Restore the primary to '%s':\n" "$tangle_default"
printf '● git -C %s checkout %s\n' "$FM_ROOT" "$tangle_default"
printf '● git -C %s switch %s\n' "$FM_ROOT" "$tangle_default"
printf "● then re-validate '%s' in a proper isolated worktree.\n" "$tangle_branch"
fi
printf '●%s\n' "$trule"
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ An absent or incompatible `gh-axi` reports `MISSING: gh-axi (install: npm instal
An absent or incompatible `lavish-axi` reports `MISSING: lavish-axi (install: npm install -g lavish-axi && lavish-axi setup hooks)`.
An absent or too-old `quota-axi` reports `MISSING: quota-axi (install: npm install -g quota-axi)`; firstmate cannot resolve a profile array without a compatible binary.
Bootstrap also reports a `TANGLE:` line when `FM_ROOT` is on a named non-default branch; follow the printed checkout remediation rather than treating it as an installable tool problem.
In a read-only session that did not get the fleet lock, the same line is advisory and omits the checkout command.
In a read-only session that did not get the fleet lock, the same line is advisory and omits the branch-restore command.
The locked session-start deferred network stage runs bootstrap's best-effort project clone refresh through `fm-fleet-sync.sh`; [`fm-bootstrap.sh`'s header](../bin/fm-bootstrap.sh) owns the exact clone-refresh overlap, liveness-before-convergence, per-mate concurrency, ordered diagnostic replay, and sequential-fallback contract.
It emits `FLEET_SYNC:` for skipped refreshes that may matter, recovered self-heals, and `STUCK:` alarms.
Normal completed runs keep local-only and no-origin skips silent.
Expand Down
4 changes: 2 additions & 2 deletions tests/fm-session-start.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ EOF
assert_not_contains "$out" "drain them with bin/fm-wake-drain.sh" "read-only guard printed a mutating drain instruction"
assert_not_contains "$out" "After draining queued wakes" "read-only guard printed a drain-then-rearm instruction"
assert_not_contains "$out" "run bin/fm-watch-arm.sh" "read-only guard printed a mutating watcher-arm instruction"
assert_not_contains "$out" "git -C $root checkout main" "read-only bootstrap printed a state-changing checkout remediation"
assert_not_contains "$out" "git -C $root switch main" "read-only bootstrap printed a state-changing checkout remediation"

# Detect-only bootstrap diagnostics still ran (the fakebin's PATH excludes
# tasks-axi, so bootstrap's own read-only tool-detection line fires
Expand Down Expand Up @@ -2219,7 +2219,7 @@ EOF

# A re-emit skips the sweeps because it ALREADY ran them, not because it lacks
# the lock, so it must still own repair rather than deferring to a lock holder.
assert_contains "$reemit" "restore the primary with: git -C $root checkout main" \
assert_contains "$reemit" "restore the primary with: git -C $root switch main" \
"--reemit disowned a repair it is entitled to perform"
assert_not_contains "$reemit" "must leave restore work to the session holding the fleet lock" \
"--reemit misreported itself as an unlocked read-only session"
Expand Down
12 changes: 6 additions & 6 deletions tests/fm-tangle-guard.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ test_guard_banner() {
out=$(run_guard "$repo")
assert_contains "$out" "WORKTREE TANGLE" "guard did not alarm on a feature branch in the primary"
assert_contains "$out" "fm/tangle-aa1" "guard banner did not name the offending branch"
assert_contains "$out" "checkout main" "guard banner did not print the restore remediation"
assert_contains "$out" "switch main" "guard banner did not print the restore remediation"
out=$(FM_GUARD_READ_ONLY=1 run_guard "$repo")
assert_contains "$out" "WORKTREE TANGLE" "read-only guard did not keep the tangle alarm"
assert_contains "$out" "read-only session must leave restore work" "read-only guard did not explain restore ownership"
assert_not_contains "$out" "checkout main" "read-only guard printed a state-changing restore command"
assert_not_contains "$out" "switch main" "read-only guard printed a state-changing restore command"
pass "fm-guard: bordered tangle banner fires only for a feature branch and suppresses repair commands in read-only mode"
}

Expand All @@ -112,18 +112,18 @@ test_bootstrap_line() {
git -C "$repo" checkout -q -B fm/tangle-bb2
out=$(run_bootstrap "$repo" | grep '^TANGLE:' || true)
assert_contains "$out" "fm/tangle-bb2" "bootstrap did not report the tangled branch"
assert_contains "$out" "checkout main" "bootstrap TANGLE line lacked the restore remediation"
assert_contains "$out" "switch main" "bootstrap TANGLE line lacked the restore remediation"
out=$(FM_ROOT_OVERRIDE="$repo" FM_HOME="$repo" FM_BOOTSTRAP_DETECT_ONLY=1 "$ROOT/bin/fm-bootstrap.sh" 2>/dev/null | grep '^TANGLE:' || true)
assert_contains "$out" "fm/tangle-bb2" "detect-only bootstrap did not report the tangled branch"
assert_contains "$out" "read-only session must leave restore work" "detect-only bootstrap did not explain restore ownership"
assert_not_contains "$out" "checkout main" "detect-only bootstrap printed a state-changing restore command"
assert_not_contains "$out" "switch main" "detect-only bootstrap printed a state-changing restore command"
pass "fm-bootstrap: TANGLE problem line fires only for a feature branch and suppresses repair commands in detect-only mode"
}

# --- GUARD 1a: brief isolation assertion ------------------------------------

# The generated ship brief must carry the isolation assertion AHEAD of the
# `git checkout -b` step, so the crewmate verifies its worktree before branching.
# `git switch -c` step, so the crewmate verifies its worktree before branching.
test_brief_assertion_precedes_branch() {
local home brief iso br
home="$TMP_ROOT/brief-home"
Expand All @@ -140,7 +140,7 @@ test_brief_assertion_precedes_branch() {
assert_no_grep "they are identical in the primary checkout" "$brief" \
"brief must not claim the primary checkout has identical git dirs"
iso=$(grep -n 'launched in primary checkout, not an isolated worktree' "$brief" | head -1 | cut -d: -f1)
br=$(grep -n 'git checkout -b fm/' "$brief" | head -1 | cut -d: -f1)
br=$(grep -n 'git switch -c fm/' "$brief" | head -1 | cut -d: -f1)
if [ -z "$iso" ] || [ -z "$br" ]; then
fail "brief missing assertion ($iso) or branch step ($br)"
fi
Expand Down