Filesystem-based coordination layer for multiple Claude Code sessions working across sibling repos on the same workstation — and across SSH-reachable hosts. Sessions write Markdown "beacons" into a shared inbox/; peer sessions read them on each loop iteration and respond by writing their own beacons or resolving via mv into _archive/.
This repo bundles three pieces:
sync-coordination.py— bidirectional Mac ↔ Windows sync ofinbox/and_archive/via SSH + scp. Archive-presence is canonical (a file in_archive/on either side wins).Harborline-Software.10s.sh— SwiftBar menubar plugin to toggle sync state, start/stop local services (Bridge / Galley / Anchor), and open logs / folders. Refreshes every 10 s.com.sunfish.coordination-sync.plist.template— macOS LaunchAgent that fires the sync script every 60 s while a.sync-activeflag file exists. Silently no-ops when paused.
Originally built for the Sunfish multi-session naval-org (CO → XO → {COB, dev, po-win, PAO → Yeoman}), but the protocol is generic — fork and customize the sender list / service surface.
git clone <this-repo> coordination
cd coordination
brew install --cask swiftbar # one-time; the menubar host
./install.sh # writes the LaunchAgent + symlinks the SwiftBar plugin + loads launchd
open -a SwiftBar # first launch — pick ~/Library/Application Support/SwiftBar/Plugins when asked
touch .sync-active # arm the sync (or click "Enable sync" in the menubar)The menubar will show 🚩 (active) or 🏳️ (paused). Click for the full control panel.
Defaults assume the repo lives next to Sunfish/ and galley/ checkouts. Override via env vars:
| Variable | Default | Used by |
|---|---|---|
COORD_MAC_DIR |
Script's own directory | sync-coordination.py |
COORD_WIN_HOST |
winhub |
sync-coordination.py (SSH alias for the Windows host) |
COORD_WIN_DIR |
C:/projects/Harborline-Software/coordination |
sync-coordination.py |
SUNFISH_DIR |
<repo>/../Sunfish |
menubar plugin |
GALLEY_DIR |
<repo>/../galley |
menubar plugin |
For env-var overrides on the LaunchAgent side, add them to the rendered ~/Library/LaunchAgents/com.sunfish.coordination-sync.plist under the EnvironmentVariables dict.
coordination/
├── README.md # this file
├── LICENSE # MIT
├── install.sh # local installer (LaunchAgent + SwiftBar plugin)
├── sync-coordination.py # bidirectional sync engine
├── Harborline-Software.10s.sh # SwiftBar menubar plugin (refresh: 10s)
├── com.sunfish.coordination-sync.plist.template # LaunchAgent template (paths substituted by install.sh)
├── inbox/ # active beacons — sessions read every loop iteration
│ └── .gitkeep # contents are .gitignored (private session state)
└── _archive/ # resolved beacons (plain `mv` from inbox/ to here)
└── .gitkeep
Naval-org chart: CO (Chris, BDFL) → XO (Sunfish research) → {COB (sunfish-PM, code), dev (galley primary + Sunfish overflow, Mac-side), po-win (Windows-side implementations + GPU host), PAO (book editor) → Yeoman (book technical writer)}.
Filename convention: {sender}-{type}-YYYY-MM-DDTHH-MMZ-{slug}.md
| Sender | Sessions | Cross-repo writes |
|---|---|---|
cob |
sunfish-PM | cob-idle-*.md, cob-question-*.md (when blocked on XO) |
dev |
galley senior developer (Mac-side) — Opus 4.7 + xhigh, used sparingly | dev-question-*.md (architecture halt), dev-status-*.md, dev-idle-*.md, dev-resumed-*.md. Primary scope: galley/. Secondary: Sunfish overflow when COB asks or CO directs. Agent definition: .claude/agents/dev.md. |
po-win |
Windows-side senior developer — Opus 4.7 + xhigh, used sparingly. Runs on the Windows host (desktop-umt08rn / winhub over Tailscale; RTX 4070 Ti). |
po-win-question-*.md, po-win-status-*.md, po-win-idle-*.md, po-win-resumed-*.md. Owns: galley services/python-workers/, apps/api/ (Windows GPU surface), apps/desktop/src-tauri/ Windows targets (x64 + ARM64), Windows-side plugin manifests, MAUI Anchor Win10/11 target (consulted by cob). Agent definition: .claude/agents/po-win.md. |
pao |
book editor | pao-question-*.md (cross-repo Sunfish-architecture questions; book-internal directives stay in the-inverted-stack/.pao-inbox/) |
yeoman |
book technical writer | yeoman-*.md only as PAO-bypass fallback when PAO is offline AND a critical Sunfish question can't wait |
xo |
Sunfish research | xo-directive-*.md, xo-ruling-*.md, xo-idle-*.md (writing to any session, including dev and po-win) |
Body: 3-line YAML frontmatter (type, workstream-or-chapter, last-pr) + ≤2 lines context + ≤2 lines "what would unblock me."
Every session reads coordination/inbox/*.md at session start and on every loop iteration:
ls /Users/christopherwood/Projects/Harborline-Software/coordination/inbox/*.md 2>/dev/nullNon-empty → priority above normal cadence. Resolve via answer / hand-off / ledger update / ADR amendment, then move the resolved beacon:
mv coordination/inbox/<beacon>.md coordination/_archive/- Tier 1 — book-internal (Yeoman ↔ PAO): stays in
the-inverted-stack/.pao-inbox/. Includespao-directive-*,yeoman-question-*,yeoman-resumed-*to PAO, plus subdirs_creative/,_decisions/,_editorial-reviews/,_state-snapshots/. Does NOT belong here. - Tier 2 — cross-repo: all other inter-session messaging (sub-XO sessions → XO, XO → book sessions, COB ↔ XO, PAO → XO). Belongs here.
_archive/*.mdolder than 30 days → delete (no PR — this dir is not git-tracked).- Active beacons in
inbox/older than 7 days unanswered → escalate to CO.
The parent Harborline-Software/ folder is not a git repo. Beacons in this directory are filesystem-coordinated only; no PR-gated archival. If audit trail is needed for a specific resolution, sessions can also git-commit a copy into Sunfish/icm/_state/ as part of the resolving PR.
When po-win is active, the Windows host has its own checkout at C:\projects\Harborline-Software\coordination\. The two coordination folders are kept in sync via coordination/sync-coordination.py (Python 3, runs on Mac, sshes to winhub).
# manual sync
python3 /Users/christopherwood/Projects/Harborline-Software/coordination/sync-coordination.py
# dry-run + verbose to inspect what would change
python3 /Users/christopherwood/Projects/Harborline-Software/coordination/sync-coordination.py --dry-run --verboseSync rules:
- A beacon present in
_archive/on EITHER side is canonical-resolved — propagate to_archive/on the other side AND delete frominbox/on both sides. - Beacons in
inbox/on only one side get copied to the other. - Filename collisions in inbox are resolved by mtime (newer wins). Beacon filenames carry UTC timestamps, so collisions only happen if both sides write to the exact same name in the same second — never in practice.
README.mdand other non-beacon files are not synced by this script (manually copy if needed).
Automatic run cadence (launchd on Mac): a LaunchAgent at ~/Library/LaunchAgents/com.sunfish.coordination-sync.plist runs the sync script every 60 seconds. It's controlled by a flag file:
# Enable sync (when po-win is active)
touch /Users/christopherwood/Projects/Harborline-Software/coordination/.sync-active
# Pause sync (when no Windows session is running)
rm /Users/christopherwood/Projects/Harborline-Software/coordination/.sync-activeWhen the flag is absent, the LaunchAgent fires every 60s but the script silently exits 0 — zero ssh / scp / launchd noise.
Other controls:
- Inspect launchd output:
tail -f coordination/.sync-stderr.log(errors),coordination/.sync-stdout.log(informational; quiet by default) - Manual one-shot sync:
python3 coordination/sync-coordination.py [--dry-run] [-v] - Unload permanently:
launchctl unload ~/Library/LaunchAgents/com.sunfish.coordination-sync.plist
Sessions writing beacons should leave the flag enabled for the duration of po-win activity. Mac-side sessions writing beacons get sync within 60s. The po-win session on Windows writes locally and waits up to 60s for the next periodic fire (or asks CO to run sync immediately).
tender-win-git-pull.ps1 runs every 10 min via Scheduled Task HarborlineTenderGitPull.
Pulls fleet + coordination repos with --ff-only (skips if working tree dirty so your
in-flight Windows-side edits aren't disrupted). Log: coordination/.git-pull.log.
This complements the Mac-side launchd sync timer — that handles coordination/inbox/,
_archive/, heartbeats/ every 60s; this handles git-tracked surfaces (agent defs, rules,
.wolf state, sync script updates themselves) every 10 min.
A SwiftBar plugin gives CO one-click menubar control over the sync flag. It shows:
- Current state (🔄 active / ⏸ paused) in the menubar
- Inbox + archive counts, last-sync line, last error
- Toggle (Enable/Pause sync), Sync now (one-shot bypassing the flag), Clear logs
- Open coordination folder / inbox / archive in Finder
- LaunchAgent submenu (Load / Unload / Reload / Edit plist)
Install (one-time):
brew install --cask swiftbar # MIT-licensed menubar plugin host
# Plugin is already symlinked at:
# ~/Library/Application Support/SwiftBar/Plugins/Harborline-Software.10s.sh
# → /Users/christopherwood/Projects/Harborline-Software/coordination/Harborline-Software.10s.sh
open -a SwiftBarOn first launch, SwiftBar asks you to pick its plugin folder via Finder — choose ~/Library/Application Support/SwiftBar/Plugins. The menubar item refreshes every 10s.
Plugin source: coordination/Harborline-Software.10s.sh — single bash script. The .10s in the filename is the SwiftBar refresh interval. Edit + save and the menubar picks it up on next refresh.