Runbook: agent-surface sync — one customization set, every IDE and bot¶
Goal¶
Every installed agent platform on the operator's machine — Claude Code, Cursor, Codex CLI, Antigravity/Gemini, and whatever comes next — carries the same set of customizations at any given point in time: skills, rules, MCP servers, plugins, marketplaces, subagents/custom agents, and top-level instruction files (CLAUDE.md / GEMINI.md / AGENTS.md and kin).
Two managed halves:
- Initial sync: inventory every installed platform's customizations, reconcile them into one canonical store, and project the union back so every platform starts from the same complete set.
- Maintenance loop: a recurring job that keeps them converged forever after — a change made anywhere (a rule edited in Cursor, an MCP server added in Claude Code, a skill deleted in Codex) is picked up on the next run and propagated everywhere else. Additions, updates, and deletions all flow.
And one front door: the whole set is manageable from vibey —
vibey surface add|update|delete|list|diff|sync — so the operator can
drive it centrally or keep editing inside their IDEs and let the loop
reconcile.
Current state (verified)¶
- vibey already enforces a repo-level version of this idea by convention:
.claude/skills/,.cursor/rules/,.agents/skills/,.agent/rules/must be updated in the same PR (CLAUDE.md's agent-surface maintenance rule). This workstream generalizes it to the machine level (user scope), makes it mechanical instead of conventional, and covers kinds the repo rule doesn't (MCP servers, plugins, marketplaces). - The docwatch runbook (04) already designs the digest-manifest pattern (content hash per item, append-only snapshots) this loop reuses.
- Nothing in vibey today reads or writes user-scope platform config.
Design¶
The canonical store¶
A git repository (default ~/.vibey/surface-store, operator-overridable)
is the source of truth. Every customization is one canonical item:
store/
skills/<name>/ # SKILL.md + assets (AgentSkills-spec shape)
rules/<name>.md # plain instruction rules
agents/<name>.md # subagent/custom-agent definitions
mcp/<name>.json # canonical MCP server def (command/url, env refs, scopes)
plugins/<name>.json # plugin identity + source (marketplace, version policy)
marketplaces/<name>.json
instructions/<name>.md # CLAUDE.md/GEMINI.md-class top-level files
manifest.json # per-item content hash × per-platform projection state + tombstones
Git gives history, rollback, and (later) multi-machine sync for free — push the store to a private remote and a second machine converges too.
Platform adapters (the projection seam)¶
One SurfaceAdapter Protocol per platform, in infrastructure/surface/:
inventory() -> [Item], project(item) -> None, remove(item_ref),
each adapter owning the native locations and formats:
| Platform | Where its customizations live (adapter's territory) |
|---|---|
| Claude Code | ~/.claude/skills/, ~/.claude/agents/, ~/.claude/CLAUDE.md, MCP via claude mcp / ~/.claude.json, plugins + marketplaces via claude plugin |
| Cursor | ~/.cursor/rules/ (user rules), Cursor MCP config, extensions/marketplace entries it exposes on disk |
| Codex CLI | ~/.codex/config.toml (MCP, instructions), ~/.codex/skills/ where supported, AGENTS.md-class files |
| Antigravity | ~/.gemini/-class config, GEMINI.md, .agent/rules/ user scope, its MCP registration |
Format translation is the adapter's job: a canonical skill projects to a
Cursor .mdc rule, a canonical MCP def projects into each tool's own
config schema, canonical instructions project to the platform's file name.
An adapter reports what kinds it supports; unsupported kinds are recorded
as not_projectable in the manifest, never silently dropped. Discovery
probes (which claude, config-dir existence) decide which adapters are
active on this machine — platforms come and go without config edits.
Sync semantics (the part that must be right)¶
- Three states per item per platform in the manifest: canonical hash, last-projected hash, last-inventoried hash. That triangle classifies every situation: new-in-platform (adopt into store → propagate), changed-in-platform (update store → propagate), deleted-in-platform (tombstone → delete everywhere), changed-in-store (project out), diverged-in-two-platforms-since-last-sync (conflict).
- Conflicts never auto-resolve. A conflict parks a vibey human gate
showing both diffs, with the answer contract picking a winner
(
--raw '{"winner": "cursor"}') or keeping both under new names. The no-silent-partial principle from the handoff gate applies verbatim. - Deletes need tombstones: "absent on platform A" is only a delete if the manifest says A previously had it; tombstones stop resurrection ping-pong between sync runs. Tombstones expire after a configurable horizon (default 90 days).
- Trust boundary: MCP servers, plugins, and marketplaces are
executable. First-seen items adopted from a platform are recorded
with provenance (which platform, when, content hash) and — by default —
propagate immediately per the operator's autosync intent, but a
surface.trust: gate_new_executablesconfig flips first-seen MCP/plugin adoption to a human gate. Secrets in MCP env blocks are stored as env-var references, never values; the store must stay free of credentials so it can be pushed to a remote.
The two halves¶
vibey surface init— the initial sync: run every adapter's inventory, build the union, park conflict gates where platforms disagree, write the canonical store + manifest, project the union back out. Idempotent; a dry-run mode (--plan) prints the full add/update/delete matrix per platform before anything is written.surface.syncjob — the maintenance loop: a serial vibey job kind scheduled on the worker heartbeat (default every 15 minutes; also runnable on demand viavibey surface sync). Each run: inventory → classify against manifest → propagate → commit the store (one conventional commit per run,chore(surface): sync — 2 adds, 1 update, 1 delete from cursor) → update manifest. The job is idempotent under replay like every vibey job; a crashed run replays cleanly because classification derives from the manifest triangle, not from memory.
CLI (the central front door)¶
vibey surface init [--plan] # initial sync
vibey surface sync [--plan] # run the loop once, now
vibey surface status # per-platform convergence table
vibey surface diff <item> # canonical vs each platform
vibey surface list [--kind mcp|skill|…]
vibey surface add <kind> <name> [--from-file|--from-platform cursor|--json …]
vibey surface update <kind> <name> …
vibey surface delete <kind> <name> # tombstones + removes everywhere
add/update/delete write the canonical store and project immediately
— no waiting for the next loop tick.
Work items (vibey decomposition seed)¶
- Canonical store + manifest model (domain: pure classification of the hash triangle into add/update/delete/conflict — this is the testable heart) + git-backed store adapter.
SurfaceAdapterProtocol + in-memory fake + port-parity tests.- Claude Code adapter (richest surface first: skills, agents, CLAUDE.md,
MCP, plugins/marketplaces via the
claudeCLI). - Cursor adapter (rules
.mdctranslation, MCP config). - Codex adapter (config.toml round-trip without clobbering unrelated keys — TOML-preserving edits).
- Antigravity adapter.
surface.syncjob kind + scheduling + conflict human gates with the--raw '{"winner": …}'contract.- CLI verb set +
--plandry-run rendering. - Trust config (
gate_new_executables) + secrets-as-references enforcement (a store scan that fails on anything credential-shaped — reuse the redaction patterns). - Live validation on this machine: seed a skill from vibey, watch it appear in all four platforms; edit a rule in Cursor, watch the loop adopt + propagate it; delete an MCP server via the CLI, watch it vanish everywhere; force a two-platform conflict and answer the gate.
Verification¶
Fixture gates green (100% floors as ever); the pure classifier
property-tested (no hash triangle maps to silence — every state has
exactly one classification). Live: the four scenarios in item 10 executed
on this machine and transcribed into an evidence file, plus a
kill-mid-sync replay proving idempotency, and vibey surface status
showing full convergence at the end.
Needs from operator¶
Nothing to start (all four platforms are installed here). A private git remote for the store if multi-machine sync is wanted later.
Risks¶
- Native formats drift (Cursor's rule format,
claudeCLI surfaces) — adapters are behind a Protocol, and docwatch (04) watches these platforms' docs already. - Clobbering hand-edited platform config: adapters do surgical, key-scoped edits (TOML/JSON preserving), never whole-file rewrites of shared config files; every projection is preceded by a manifest check that the platform copy matches last-projected state (else it's a change to adopt, not overwrite).
- Sync loops fighting the user mid-edit: the loop only adopts a change when the file has been stable for one full interval (mtime debounce).
- Executable trust spread is the real hazard — provenance recording is mandatory, the gate flag exists, and the store's git history is the audit log.