Skip to content

fix(bin): keep fleet clones cleanly synced - #18

Merged
kunchenguid merged 5 commits into
mainfrom
fm/fleet-sync-b6
Jun 14, 2026
Merged

kunchenguid merged 5 commits into
mainfrom
fm/fleet-sync-b6

Conversation

@kunchenguid

Copy link
Copy Markdown
Owner

Intent

Fix firstmate's pooled project clones drifting behind their remote defaults by adding a guarded fleet-sync helper that fetches origin and clean-fast-forwards each clone's checked-out default branch only when safe. The helper must skip local-only/no-origin projects, dirty clones, clones not on default, and diverged/non-fast-forward cases with concise one-line reasons, never force, stash, merge, or touch treehouse worktrees, and continue best-effort across all repos. Bootstrap should run the fleet refresh best-effort and stay quiet unless a repo could not sync, and successful PR-based teardown should refresh that project's clone after merge confirmation. Documentation in AGENTS.md must record the narrow write exception and bootstrap/teardown behavior, with CLAUDE.md tracking through the symlink; README should stay consistent with the toolbelt.

What Changed

  • Added bin/fm-fleet-sync.sh to fetch origin and clean-fast-forward checked-out default branches only when safe, skipping local-only, no-origin, dirty, non-default, and diverged clones with concise reasons.
  • Wired best-effort fleet refresh into bootstrap and successful PR-based teardown, including timeout cleanup and suppression of expected local-only bootstrap noise.
  • Updated AGENTS.md and README.md to document the narrow fleet-sync write exception and the new bootstrap/teardown refresh behavior.

Risk Assessment

✅ Low: The change is narrowly scoped to guarded clone refresh behavior, with safety checks for local-only, dirty, non-default, and non-fast-forward cases and no material review findings.

Testing

Exercised the user-facing shell workflow with real git repositories: the helper fast-forwarded only a clean default-branch clone, skipped dirty/non-default/diverged/local-only/no-origin cases safely, bootstrap suppressed expected local-only/no-origin noise while surfacing real skipped syncs, teardown refreshed the project clone after a simulated merge, and the worktree was clean afterward.

Evidence: Fleet sync and bootstrap CLI transcript

Shows sync-safe: synced 98b7d1b..73e2118, unsafe clones skipped with concise reasons, and bootstrap emitting only dirty/diverged/non-default FLEET_SYNC: lines while suppressing local-only/no-origin skips.

Fleet sync CLI verification transcript
root=/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M
evidence=/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-cli-transcript.txt

$ git init --bare /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git
Initialized empty Git repository in /private/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git/

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git symbolic-ref HEAD refs/heads/main

$ git init -b main /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed
Initialized empty Git repository in /private/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed/.git/

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed config user.email test@example.com

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed config user.name Test User

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed add app.txt

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed commit -m initial
[main (root-commit) 98b7d1b] initial
 1 file changed, 1 insertion(+)
 create mode 100644 app.txt

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed remote add origin /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed push -u origin main
To /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

$ git clone /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/sync-safe
Cloning into '/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/sync-safe'...
done.

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/sync-safe config user.email test@example.com

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/sync-safe config user.name Test User

$ git clone /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/dirty
Cloning into '/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/dirty'...
done.

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/dirty config user.email test@example.com

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/dirty config user.name Test User

$ git clone /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/feature
Cloning into '/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/feature'...
done.

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/feature config user.email test@example.com

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/feature config user.name Test User

$ git clone /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/diverged
Cloning into '/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/diverged'...
done.

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/diverged config user.email test@example.com

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/diverged config user.name Test User

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/feature checkout -b feature
Switched to a new branch 'feature'

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/diverged add app.txt

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/diverged commit -m local-diverge
[main 6892bd9] local-diverge
 1 file changed, 1 insertion(+)

$ git init -b main /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/noorigin
Initialized empty Git repository in /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/noorigin/.git/

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/noorigin config user.email test@example.com

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/noorigin config user.name Test User

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/noorigin add local.txt

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/noorigin commit -m local-only
[main (root-commit) 6837759] local-only
 1 file changed, 1 insertion(+)
 create mode 100644 local.txt

$ git init -b main /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/localonly
Initialized empty Git repository in /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/localonly/.git/

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/localonly config user.email test@example.com

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/localonly config user.name Test User

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/localonly add local.txt

$ git -C /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/localonly commit -m registered-local-only
[main (root-commit) f19d16b] registered-local-only
 1 file changed, 1 insertion(+)
 create mode 100644 local.txt

Advancing origin/main to create drift in pooled clones.

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed add app.txt

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed commit -m remote-advance
[main 73e2118] remote-advance
 1 file changed, 1 insertion(+)

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/seed push origin main
To /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/fleet-sync-lab/remote.git
   98b7d1b..73e2118  main -> main

Remote main after advance: 73e2118
sync-safe before sync: 98b7d1b
dirty before sync: 98b7d1b
feature before sync: 98b7d1b
diverged before sync: 6892bd9

Fleet sync across all registered project clones:

$ /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/bin/fm-fleet-sync.sh
dirty: skipped: dirty working tree
diverged: skipped: local main has diverged from origin/main
feature: skipped: on feature, expected main
localonly: skipped: local-only project
noorigin: skipped: no origin remote
sync-safe: synced 98b7d1b..73e2118
sync-safe after sync: 73e2118
dirty after sync: 98b7d1b
feature after sync: 98b7d1b
diverged after sync: 6892bd9
sync-safe matches origin: yes
dirty unchanged: yes
feature branch unchanged: yes
diverged unchanged: yes

Bootstrap user-visible fleet-sync lines (expected local-only/no-origin skips suppressed):
FLEET_SYNC: dirty: skipped: dirty working tree
FLEET_SYNC: diverged: skipped: local main has diverged from origin/main
FLEET_SYNC: feature: skipped: on feature, expected main

Done.
Evidence: Teardown refresh CLI transcript

Shows teardown-sync: synced 468a96e..4b26696 and teardown refreshed project to origin: yes after a simulated PR merge.

Teardown post-merge fleet refresh transcript
root=/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M
evidence=/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-cli-transcript.txt

$ git init --bare /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/remote.git
Initialized empty Git repository in /private/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/remote.git/

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/remote.git symbolic-ref HEAD refs/heads/main

$ git init -b main /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed
Initialized empty Git repository in /private/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed/.git/

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed config user.email test@example.com

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed config user.name Test User

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed add app.txt

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed commit -m initial
[main (root-commit) 468a96e] initial
 1 file changed, 1 insertion(+)
 create mode 100644 app.txt

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed remote add origin /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/remote.git

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed push -u origin main
To /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/remote.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

$ git clone /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/remote.git /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/teardown-sync
Cloning into '/Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/projects/teardown-sync'...
done.

$ git clone /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/remote.git /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/task-worktree
Cloning into '/var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/task-worktree'...
done.

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed add app.txt

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed commit -m merged-pr-advance
[main 4b26696] merged-pr-advance
 1 file changed, 1 insertion(+)

$ git -C /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/seed push origin main
To /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/remote.git
   468a96e..4b26696  main -> main
project before teardown refresh: 468a96e
origin after simulated merge: 4b26696

$ /Users/kunchen/.no-mistakes/worktrees/016d88035d58/01KV3Z0WTQ2KF9QW4DWFN9GY9M/bin/fm-teardown.sh teardown-sync
WARNING: tasks are in flight but no watcher has ever run (no liveness beacon).
Restart it NOW, before anything else: run bin/fm-watch.sh as a background task.
treehouse stub: 
tmux stub: 
teardown-sync: synced 468a96e..4b26696
teardown teardown-sync complete (window fm-teardown-sync, worktree /var/folders/5x/4nqprlbx0518k3ybcb1sz6gr0000gn/T/no-mistakes-evidence/01KV3Z0WTQ2KF9QW4DWFN9GY9M/teardown-sync-lab/task-worktree)
project after teardown refresh: 4b26696
teardown refreshed project to origin: yes

Done.
- Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (7m10s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed (2) ✅
  • ⚠️ bin/fm-fleet-sync.sh:59 - fm-fleet-sync.sh only skips projects without an origin remote and never checks data/projects.md for local-only mode. A project registered as local-only but still carrying an origin remote would be fetched and potentially fast-forwarded, violating the intended local-only/no-remote write boundary; skip mode=local-only before fetching or merging.

🔧 Fix: Skip local-only projects during fleet sync
1 warning still open:

  • ⚠️ bin/fm-bootstrap.sh:30 - The bootstrap timeout only kills the wrapper subshell, not the fm-fleet-sync.sh process it launched, so a slow fetch/sync can continue fast-forwarding project clones after bootstrap has reported FLEET_SYNC: fleet: skipped: bootstrap refresh timed out and deleted its temp files. Run the sync process in a killable process group or avoid claiming the refresh was stopped until the actual sync process has exited.

🔧 Fix: Fix bootstrap sync timeout cleanup
✅ Re-checked - no issues remain.

🔧 **Test** - 1 issue found → auto-fixed ✅
  • 🚨 bin/fm-bootstrap.sh:43 - Bootstrap reports a registered local-only project as FLEET_SYNC: ... skipped: local-only project, even though local-only projects are expected to have no remote and the intended behavior says bootstrap should stay quiet for expected skips unless a repo could not sync.
  • bin/fm-fleet-sync.sh <fixture-clone> against throwaway Git repos covering clean fast-forward, already-current, non-default branch, dirty working tree, diverged local branch, and no-origin repository
  • FM_FLEET_SYNC_BOOTSTRAP_TIMEOUT=10 bin/fm-bootstrap.sh with a temporary registered [local-only] project under projects/ and data/projects.md
  • git status --short after cleanup to verify no working-tree artifacts remained

🔧 Fix: Suppress local-only bootstrap sync noise
✅ Re-checked - no issues remain.

  • Inspected the target diff from 363fa89f80a5e48147b15764e0ebcf1fe9c368ce...d7b8cd37c3b585fd9b881b8edc17da9954eb187c to identify the intended behavior and changed scripts/docs.
  • Ran an end-to-end CLI harness that created real temporary git remotes/clones, drifted origin/main, and executed bin/fm-fleet-sync.sh across safe, dirty, non-default-branch, diverged, local-only, and no-origin project clones.
  • Ran FM_FLEET_SYNC_BOOTSTRAP_TIMEOUT=20 bin/fm-bootstrap.sh with temporary registered projects and filtered its user-visible FLEET_SYNC: output to verify expected local-only/no-origin skips stay quiet while unsafe skipped syncs are surfaced.
  • Ran bin/fm-teardown.sh teardown-sync with temporary git repos plus stubbed treehouse and tmux commands to verify a completed PR-style teardown invokes fm-fleet-sync.sh and fast-forwards the project clone after the simulated merge.
  • Ran git status --short after cleanup to verify transient worktree test projects/state were removed.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@kunchenguid
kunchenguid merged commit 539f3a3 into main Jun 14, 2026
3 checks passed
@kunchenguid
kunchenguid deleted the fm/fleet-sync-b6 branch June 14, 2026 21:23
@birbprophet

Copy link
Copy Markdown

Coverage note after merging current main:

The current PR gate is green, but the replacement under-five-minute workflow does not execute the Herdr or portable test families. The Herdr evidence for this branch is the earlier run on head d3d63f05, where Behavior tests (Herdr) passed at 6m0s. The smaller green gate must not be read as broader coverage.

vipentti pushed a commit to vipentti/firstmate that referenced this pull request Aug 5, 2026
* Add fleet default-branch sync

* no-mistakes(review): Skip local-only projects during fleet sync

* no-mistakes(review): Fix bootstrap sync timeout cleanup

* no-mistakes(test): Suppress local-only bootstrap sync noise

* no-mistakes(document): Synchronize fleet-sync documentation
NewAiCoder added a commit to NewAiCoder/firstmate that referenced this pull request Sep 26, 2026
… grace (kunchenguid#18)

* fix(bin): derive the away-mode beacon grace from the poll cadence

fm-turnend-guard.sh's away-mode branch required the watcher beacon to
be fresh within the flat FM_GUARD_GRACE default (300s), but the daemon
starts a fresh one-shot watcher only after it finishes handling the
previous wake, and that handling can legitimately outrun a fixed 300s
window under load (a slow registered check, a busy supervisor pane)
with the daemon perfectly healthy throughout. That misread a live,
correctly-cycling daemon as down and blocked the turn.

Add fm_poll_derived_grace, the single owner of the existing
max(300, FM_POLL + 60) formula two other callers already use, and have
the away-mode branch derive its beacon grace from it instead of the
flat default. A dead daemon pid or a beacon older than that grace
still blocks, so a genuinely lapsed away mode still alarms; every
other check on this page is unchanged.

* no-mistakes(review): Dedup guard-grace formula to fm_poll_derived_grace in watch and autoarm

* no-mistakes(test): summary: fix PATH-collision test hermeticity for node/orca missing-tool simulations

* no-mistakes(test): fix(test): accept systemd-user subreaper as orphan target, not just ppid 1

* no-mistakes(document): docs: note away-mode poll-derived-grace tests in coverage list

* no-mistakes(lint): fix(test): use read -ra to fix SC2206 word-splitting warning

* no-mistakes(ci): Root cause: tests/fm-tool-update-check.test.sh declared a plain string variable named `tools` (built via `local ... tools=` then `tools="$tools,..."`) in two test functions. tests/lib.sh (sourced by the test file) independently declares a local array `local tools=("$@")` in fm_test_base_path_sans. ShellCheck's --external-sources cross-file analysis (used by fm-lint.sh) conflates the two same-named locals across files and flags the string usage as SC2178 (var used as array but assigned a string) and SC2128 (expanding an array without an index). Fix: renamed the string variable in both affected test functions (test_an_overlong_report_says_it_was_cut and test_a_finding_past_the_cut_is_still_reported) from `tools` to `tools_json`, eliminating the name collision. No behavior change. Verified: `shellcheck --shell=bash -x tests/fm-tool-update-check.test.sh` now exits 0 with zero warnings (previously reproduced SC2178/SC2128 exactly as in CI). Ran the full test file directly (`bash tests/fm-tool-update-check.test.sh`) — all 37 tests pass, including the two touched by the rename. The other lint-log lines (systemd-run "Killed" during memory-bounded shellcheck runs, and the external-sources fallback retries) are pre-existing infra behavior of fm-lint.sh's OOM-bounded scanning and retry fallback — they resolved themselves within the same run (logged as "retried without it and finished cleanly") and are not caused by this change; only the SC2178/SC2128 warnings caused the actual exit code 1

---------

Co-authored-by: NewAiCoder <170579485+NewAiCoder@users.noreply.github.com>
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