Introduce the cross-project capture->triage->roadmap loop: /capture appends an idea or bug to INBOX.md from any repo (new-project ideas included), /triage drains a project's items into its AGENTS.md or ROADMAP.md. Give the standards repo its own AGENTS.md (+ CLAUDE.md symlink) and ROADMAP.md so it follows its own standard, and add a 'What git tracks' section to portability.md plus the canonical .gitignore block answering what is committed vs gitignored around .claude and symlinks.
9.8 KiB
Portability Protocol
Purpose: keep every layer of my agent setup hot-swappable across model providers. Any compliant tool dropped into one of my repos should be productive immediately; adopting a new tool should cost minutes; returning to a previous tool should cost nothing.
Lives at: ~/Projects/standards/portability.md. The standards repo is laid out as:
~/Projects/standards/
AGENTS.md ← CLAUDE.md (relative symlink) ← agent-facing orientation to this repo
README.md how-i-work.md portability.md retrofit-playbook.md subagents-handbook.md
ROADMAP.md ← this repo's backlog (future agents, commands, standards)
INBOX.md ← cross-project capture buffer (/capture → here, /triage drains it)
guides/ ← neutral substance (vendor-agnostic): checklists, role knowledge
adapters/
claude/
commands/ ← ~/.claude/commands symlinks here (global commands, e.g. /handoff)
agents/ ← ~/.claude/agents symlinks here (global subagents)
Companions: how-i-work.md (the always-loaded user layer, ~50 lines), retrofit-playbook.md (the one-time conversion manual), and subagents-handbook.md (designing and running delegated agents). This document is reference material — never symlink it into anything always-loaded.
The principle
Knowledge lives in vendor-neutral files; vendor-named paths are thin adapters — symlinks or pointers — into them.
Corollaries:
- Repos are self-contained. Everything required to work on a project correctly lives in the repo. Global and user-level files add preferences, never requirements.
- Swap at session boundaries. The close-out ritual (update Current state → commit → push) is the handoff protocol between providers, not just between sessions.
- Convert machinery, never knowledge. At adoption time, an agent regenerates wrappers (subagents, commands, rule loaders). The substance they point at never moves. Every command and subagent is a thin wrapper in
adapters/<tool>/whose substance lives inguides/— no inline exceptions, so any wrapper converts to a new tool by swapping its header. - Session state is disposable by design. Conversations and per-tool caches (e.g. Claude's auto memory) are conveniences. Anything important gets promoted into the files below.
- No secrets in any of these files. Secrets live in gitignored .env files; documents reference env-var names only.
The layers
| Layer | Neutral source of truth | Claude adapter | Portability status |
|---|---|---|---|
| Project knowledge | <repo>/AGENTS.md |
symlink CLAUDE.md → AGENTS.md |
Open standard — Cursor, Copilot, Codex, Gemini CLI, opencode read it |
| Project status (now) | ## Current state section in AGENTS.md |
(same symlink) | Fully portable; maintained by the close-out ritual |
| Project backlog (later) | ROADMAP.md at repo root |
— (plain committed file, no symlink) | Fully portable; committed and pushed like any file, read only on demand |
| Scoped guides | <repo>/docs/guides/<topic>.md with paths: frontmatter, plus one index line each in AGENTS.md |
symlinks from .claude/rules/ (auto lazy-load) |
Content fully portable; lazy-loading is per-tool. Other tools find guides via the index lines |
| User preferences | ~/Projects/standards/how-i-work.md |
symlink ~/.claude/CLAUDE.md → it |
No cross-tool standard above repo level; each tool's global file symlinks here at adoption |
| Skills (procedures) | folders of SKILL.md + plain bash/python scripts |
.claude/skills/ |
Format is open markdown; a cross-tool home (.agents/skills/) is emerging. Worst case: a pointer line in AGENTS.md |
| Subagents (delegation) | guides folder at matching scope — <repo>/docs/guides/ for project agents, standards/guides/ for global |
thin wrapper in .claude/agents/ (project) or standards/adapters/claude/agents/ (global, symlinked as ~/.claude/agents) |
No standard exists. Wrappers regenerated per tool at adoption |
| Commands (saved prompt shortcuts) | substance in guides/ at matching scope, same rule as subagents |
thin wrapper in .claude/commands/ (project) or standards/adapters/claude/commands/ (global, symlinked as ~/.claude/commands) |
No standard. Same treatment as subagents |
Scope rule: every artifact lives at the scope it describes — project-specific in that repo; universal in the standards repo. There, neutral substance goes in guides/ and vendor machinery is quarantined under adapters/<tool>/ (so a second provider's wrappers never intermix with Claude's or with the shared guides).
What git tracks
Symlinks and .claude/ raise a recurring question: what belongs in the repo, what stays
local, and how does the global ~/.claude layer get backed up? One rule resolves all three
— git tracks knowledge and the wiring that serves it; it ignores machine-local state and
secrets — but it lands in three places.
1. Relative symlinks are committed and travel. Git stores a symlink as its target path.
Because every vendor path in this standard is a relative symlink (CLAUDE.md → AGENTS.md,
.claude/rules/x.md → ../../docs/guides/x.md), it commits and clones correctly on any
machine. This is the concrete payoff of the relative-symlink mandate: an absolute symlink
would commit too, but break on every other clone. Never commit an absolute symlink.
2. Inside a project repo — commit shared, ignore local.
Committed (any agent or teammate needs them to work the repo correctly — corollary 1):
AGENTS.md and its CLAUDE.md symlink; docs/guides/*.md and their .claude/rules/*.md
symlinks; ROADMAP.md; .claude/settings.json (shared project settings and hooks —
deterministic behavior is part of the repo); .claude/agents/*.md, .claude/commands/*.md,
.claude/skills/ (project-scoped wrappers).
Gitignored (per-user, per-machine, or secret): .claude/settings.local.json and any
*.local.* (personal permissions/overrides); .env and secrets (corollary 5); OS cruft.
Put these in the repo's own committed .gitignore — don't rely on a global
excludesfile, which a fresh clone or another machine won't have. Canonical block:
# Secrets & local env
.env
.env.*
!.env.example
# Claude Code — commit shared config, ignore personal/local
.claude/settings.local.json
.claude/*.local.json
# OS cruft
.DS_Store
3. The global ~/.claude layer is backed up through the standards repo, not on its
own. ~/.claude is not a git repo. Its durable, portable parts — commands, agents,
CLAUDE.md — are symlinks into this standards repo, so committing and pushing standards
backs them up. Everything else under ~/.claude (the machine-local settings.json,
projects/ session transcripts and auto-memory, history, todos, caches) is disposable
session state by design (corollary 4) and is neither committed nor backed up. What makes
this safe: promote anything durable out of auto-memory into a committed file (AGENTS.md or a
guide); audit with /memory.
So the answer to "are we backing up all of .claude?" is no, by design: knowledge and
shared wiring are committed per-repo or symlinked into a backed-up repo; machine-local state
and secrets never are.
Swap protocol (between already-adopted tools)
- Finish the task and run the close-out ritual; exit the session.
- Open the other tool in the same repo.
- It reads AGENTS.md (plus its own global file) and Current state, and continues.
Nothing else. If step 3 stumbles, the missing fact belongs in AGENTS.md — add it and commit.
Adoption checklist (first time using a new provider)
Run by the new agent itself: "Read ~/Projects/standards/portability.md and onboard yourself as a new provider."
- Install and authenticate the tool — the only human-heavy step.
- Symlink (or copy) its global instruction file to
standards/how-i-work.md. - Confirm it reads
<repo>/AGENTS.md. If it expects a different filename, symlink that name to AGENTS.md. - If it supports path-scoped or lazy loading, wire its mechanism to
docs/guides/; otherwise the AGENTS.md index lines suffice. - If it supports skills or procedures, point it at the skills folders; otherwise add a pointer line in its global file.
- Regenerate the thin wrappers it supports (subagents, commands) from the guides they reference, placing global ones in
adapters/<tool>/and symlinking the tool's config home to them. - Record what was done in a new provider section at the bottom of this document.
Provider adapters
Claude Code (current)
CLAUDE.md→ symlink →AGENTS.mdat the root of every repo.claude/rules/<topic>.md→ symlinks →docs/guides/<topic>.md(auto lazy-load viapaths:frontmatter)~/.claude/CLAUDE.md→ symlink →standards/how-i-work.md.claude/agents/— thin subagent wrappers pointing at docs/guides (project-specific agents only).claude/commands/— thin command templates, regenerable (project-specific commands only)- Global commands and agents:
~/.claude/commands→ symlink →standards/adapters/claude/commands/;~/.claude/agents→ symlink →standards/adapters/claude/agents/(versioned and backed up with the standards repo) .claude/skills/— skills home for now; migrate to.agents/skills/if that convention solidifies- Auto memory: local cache only (
~/.claude/projects/<project>/memory/), outside git, not portable. Promote keepers into AGENTS.md or guides. Audit with/memory.
Next provider — template
- Global file location: ___ → symlink →
standards/how-i-work.md - Reads AGENTS.md natively? ___ (if not: symlink its expected filename → AGENTS.md)
- Scoped/lazy loading mechanism: ___ (wired to docs/guides, or relying on index lines)
- Skills support: ___
- Delegation/subagent equivalent: ___ (wrappers regenerated on: ___)
- Notes and quirks: ___