Skip to content

SOPs

Repo Sync Policy

Until 2026-04-30 every repo lived on every machine — Studio, MacBook Pro, VPS, Remote Mac. That worked at five repos. With 134 it means:

  • Pullall and post-push-sync iterate across the whole fleet on every operation, even when 80% of repos have no business on the target machine.
  • Dirty-check pings every repo on every machine, which fills the pre-prompt status line with noise (a Chrome extension showing dirty on the VPS is meaningless).
  • A half-rewritten repo can show up dirty on a machine that has no use for it.

Per-repo allocation cuts the iteration count, narrows the blast radius, and makes “what is on this machine and why” intentional instead of historical.

Every repo declares its allocation as a single line at the top of <repo>/CLAUDE.md, immediately after the H1 title:

# <repo-name>
> **Sync:** studio, mbp

That is the source of truth. Tooling reads this line; humans read this line. One line, one place.

Always enumerate the actual machines. The valid tokens are:

  • studio
  • mbp
  • vps
  • remote-mac

Plus one shorthand:

  • all — equivalent to studio, mbp, vps, remote-mac

Banned: any grouped moniker like desk, studio-only, vps-only, desk+vps. The convention is “list the machines, full names, no decoder ring.” If you find a moniker in an existing repo, expand it to the explicit machine list. The parser rejects unknown tokens and refuses to run — that is the enforcement.

Repo typeSync line
Default — dev tooling, daily-driver Mac appsSync: studio, mbp
Chrome extension or Chrome automationSync: studio, mbp, remote-mac
Production VPS service with local developmentSync: studio, mbp, vps
Foundational shared toolingSync: all
Heavy local-only tooling (iCloud watcher, NAS scan)Sync: studio
VPS-resident service with no active local devSync: vps

When you create a new repo, the default Sync line is:

> **Sync:** studio, mbp

That covers active development on both desk Macs. Add vps if the repo runs a service there, add remote-mac if it drives Chrome unattended, switch to all if it is foundational shared tooling.

Every machine needs this to participate in the fleet. Examples (not exhaustive):

  • cc — root configuration, hooks, repo registry
  • voice-pipeline — speak-claude-response.py, voice classifier, must run on every machine that hears James
  • gmail-helper — used cross-machine
  • pullall — the sync tool itself; if it is missing, you cannot get it
  • auto-journal — runs from any machine that observes the day
  • tms-internal — the wiki, useful from every machine
  • Customer CRM repos that touch shared business data — claude-code-crm, ai-assistant, allthingshandy, gokartpark-website

Sync: studio, mbp — dev tooling and daily-driver Mac apps

Section titled “Sync: studio, mbp — dev tooling and daily-driver Mac apps”

Default for things you build, edit, or run interactively on a desk Mac:

  • github-helper — files issues and PRs from the dev machine, no business on a headless server
  • freeflow-james, freeflow-button — Mac apps with Xcode/Makefile builds
  • doodles, james-voice — creative output, edited locally
  • Blog and website repos in active iteration — themarketingshow, mytechsupport
  • One-off Mac utilities — finder-copy-path, finder-move, mission-control

Sync: studio, mbp, remote-mac — Chrome extensions and Chrome automation

Section titled “Sync: studio, mbp, remote-mac — Chrome extensions and Chrome automation”

Per the global CLAUDE.md “Chrome automation” rule, unattended browser work runs only on Remote Mac. The repos that drive that work need to live there. Development still happens on the desk Macs, so it is studio + mbp + remote-mac, not remote-mac alone:

  • fb-name-learner, fb-spam-remover, fb-group-monitor, fb-messenger-ghl, fb-messenger-voice-noter, fb-messenger-voice-transcriber, fb-birthday-greeter, fb-email-slurper-3000, fb-follower-tracker
  • conference-talk-reader — Chrome extension
  • linkedin-followers-ghl, linkedin-auto-poster — browser automation against LinkedIn
  • fb-auto-poster — Chrome AppleScript poster

Sync: studio, mbp, vps — production VPS service with local development

Section titled “Sync: studio, mbp, vps — production VPS service with local development”

The repo runs a service on the VPS but you also touch the code on a desk Mac:

  • webhook-receiver — VPS webhook router
  • remote-claude — VPS web interface
  • dmarc-monitor — VPS DMARC SMTP server
  • update-manager — VPS cron orchestrator
  • Any FastAPI service with a systemctl unit on the VPS

Sync: vps — VPS-resident service with no active local dev

Section titled “Sync: vps — VPS-resident service with no active local dev”

Rare. Use only when you genuinely never edit it from a desk Mac. Most production services move into studio, mbp, vps because dev iteration eventually happens.

Things that physically cannot or should not roam:

  • meal-tracker — iCloud Photos watcher, runs on Studio
  • nas-crawler — Drive-mounted NAS scan, Studio’s mount
  • Any launchd job tied to a Studio-specific path

Sync: remote-mac — Remote-Mac-resident only

Section titled “Sync: remote-mac — Remote-Mac-resident only”

Rare. Use when the agent code physically lives on remote-mac and is not edited from a desk Mac (e.g., openclaw-specific files).

The following tools read the Sync header on startup and filter their work to the current machine:

  • pullall/pullall — clones repos that should be on this machine, pulls existing ones, removes ones that should NOT be on this machine only with explicit confirmation.
  • cc/post-push-sync.sh — after a push, only triggers a pull on machines whose tag includes the pushed repo.
  • pullall/check-dirty.py — only reports dirty state for repos that should be on this machine. Stops surfacing noise for repos that have no business here.
  • Unknown token (e.g., desk): parser refuses to run, prints which repo and which token, exits 1. Forces the writer to fix the header.
  • Missing Sync header entirely: parser treats as all during the rollout window (so the system keeps working while we sweep). Once the sweep is complete, missing-header behavior switches to “fail loud” — every repo must declare.

To add a machine: edit the > **Sync:** line in <repo>/CLAUDE.md, push. Next pullall on the new machine clones it. No extra step.

To remove a machine: edit the line, push. Next pullall on the now-excluded machine asks for explicit confirmation before deleting that repo’s local clone. Pruning is destructive — the prompt is non-negotiable.

The categorization sweep across all 134 repos runs as a one-time background pass on 2026-04-30. Output: a proposed tag per repo with a one-line justification. Reviewed in batches before any header is written. Final matrix lives at infrastructure/repo-machine-matrix (auto-generated from headers).

The convention here is enforced by the parser refusing to run on unknown tokens and the Sync header being a required field once the sweep lands. No memory needed — the policy IS the source of truth, and the tooling checks against it.