Phase 2 made the squad run on every PR. Phase 3 makes a team share a single growing knowledge base. /squad-sync synchronises learnings.jsonl, patterns.md, codebase-map.md, and review-history.md with a team-owned git remote so multiple developers collaborate through shared squad intelligence. agent-notes/ remains per-user and is never synced. Security is the hardest part: URLs, schemes, credentials, and fork-push safety must all fail closed before any write reaches disk or any command reaches the remote.
-
/squad-sync --init <remote-url> — one-time bootstrap
Validates the URL before anything is written: rejects credential-embedded forms (https://user:pass@host) and non-https / git / git@ schemes. Verifies .review-squad/ is in .gitignore as a blocking step zero (appends if missing). Creates a private squad-state-<project> GitHub repo via API with a 10-second timeout and a single 5xx retry. Writes .review-squad/<project>/config.json with all four schema fields (remote_url, strategy, sync_branch, team_members). Registers the squad-state git remote idempotently: same URL exits silently, different URL requires confirmation.
-
/squad-sync --push — publish local state
Re-reads and re-validates config.json (SSRF-safe — never trusts the original --init argument). Fork push safety check aborts if config.remote_url ≠ git remote get-url squad-state. Clones into a chmod 700 temp dir with trap ... EXIT cleanup. Copies only the four shared files by name — never git add .. Commits with an explicit user.name=squad-sync / user.email=squad-sync@local identity. Writes a .last-push sentinel for reliable timestamp reporting.
-
/squad-sync --pull — apply remote state
Re-validates config; performs fork safety check. Clones the remote into a second chmod 700 temp dir. Runs union dedup on learnings.jsonl — full-line string match, git merge is never used on this file (merge conflicts on append-only JSONL produce garbage). Remote-wins overwrite for patterns.md, codebase-map.md, review-history.md. Writes a .last-pull sentinel on completion.
-
/squad-sync --status — honest sync state
Reads .last-push and .last-pull sentinels for timestamps that don’t lie. git fetch squad-state main populates the local tracking ref before comparison (this was the round-8 fix: the tracking ref was never populated by the mktemp-clone operations, so the status output was always never synced even after a successful push). Reports one of four states: [synced] / [remote has changes — run --pull] / [never synced — run --pull] / [remote unreachable].
-
/squad-sync --merge — conflict remediation guide
Scans patterns.md, codebase-map.md, review-history.md for conflict markers (<<<<<<<, =======, >>>>>>>) with precise line numbers. Prints step-by-step resolution instructions (keep both, keep remote, keep local) with the exact shell commands the developer needs to run. Does not attempt automatic resolution — humans decide which knowledge wins.
-
docs/squad-sync-config-schema.md — formal config contract
Documents the config.json schema, the strategy enum values (remote-wins / local-wins / prompt), the team_members structure (object keyed by GitHub username with display names), the per-user agent-notes/<user>/ naming convention introduced to prevent cross-developer memory pollution, and the migration path from earlier installs that used flat agent-notes/.
-
GITHUB_TOKEN is environment-only
Never prompted at the terminal. Never stored on disk. Never logged. The single scope is "create repo" — once --init has written config.json, the token is no longer read. Subsequent --push / --pull use gh’s own credential cache.
-
Credential-embedded URLs rejected before any config write
The scheme validator runs on the initial
--init argument AND on every --push / --pull re-read of config.json — defense against a tampered config file replacing the safe URL with a credential-embedded one. Rejection happens before any git operation or config persistence.
-
agent-notes/ is never synced
Per-user content stays local. The copy-list in --push names the four shared files explicitly; agent-notes/ is not in that list and cannot be added without a code change. This is how Cory’s memory of one developer’s style doesn’t leak across the team.
-
Fork push safety check on every
--push
If config.remote_url doesn’t match git remote get-url squad-state, the push aborts. Prevents the scenario where a developer forks the squad-state repo, points their local squad-state remote at the fork, then pushes — which would appear to succeed while sending team knowledge to an unauthorized place.
-
Rounds 1–7: blockers cleared across security, connectivity, and craft
Early rounds caught URL validator gaps (the first implementation accepted
https://u:p@host), copy-list drift (a git add . was proposed and shot down by Jared), confirmation UX on --init with an existing remote, sentinel file timing, and the discriminated strategy enum. Each round produced findings with file:line citations; none were phantom.
-
Round 8 — three hardening fixes before merge
Tracking ref fix:
--status compared refs/remotes/squad-state/main without first running git fetch squad-state main — the ref was never populated, so --status always reported never synced even after a successful push. Added the fetch step. jq null guard: jq -r '.remote_url' on a malformed config emits the literal string null, which downstream bash code then treated as a URL — changed to jq -r '.remote_url // empty' in all three call sites (Gotcha #3 compliance). Safe arithmetic: ((n++)) post-increment evaluates to 0 on the first call, which under set -e aborts the entire shell script. Replaced with n=$((n + 1)) throughout.
-
Meta observation: squad-self-review on squad-modifying meta-work pays for itself
Same pattern as Phase 2: eight rounds of review on a team-infrastructure change caught real issues each round. The standing preference to skip squad-self-review on meta-work explicitly does not apply when the meta-work changes how developers store and share squad knowledge — bugs there would cascade across every future sync, silently.
-
commands/squad-sync.md
New slash command with five subcommands. Includes a Gotchas section per the V4.2+ convention.
-
docs/squad-sync-config-schema.md
New schema reference covering config fields, strategy enum, team_members structure, per-user agent-notes naming, and migration path.
-
README.md
V5.0 Phase 3 callout added to the top; /squad-sync row added to the commands table; .review-squad/config.json + agent-notes/<user>/ added to the repo structure map.
-
.gitignore
Removed duplicate .review-squad/ entry and a redundant .claude/handoff.md line surfaced during the cleanup pass.