Decisions

ADR-126: Durable engineering content lives in git, not in ephemeral substrates

Multiple times this session (and many sessions before) operators and agents have written *durable engineering content* — design rationales, conventions, fleet-wide rules, accumulated feedback memories — into substrates…

#126

ADR-126: Durable engineering content lives in git, not in ephemeral substrates

Status: Proposed Date: 2026-05-02

Context

Multiple times this session (and many sessions before) operators and agents have written durable engineering content — design rationales, conventions, fleet-wide rules, accumulated feedback memories — into substrates that are fundamentally ephemeral:

  • Warp tickets: Postgres-backed coordination service. Row content lives until a DB wipe / migration / service replacement. The body of a warp ticket frequently grows into a multi-page design doc with rationale, scope, alternatives, acceptance criteria — engineering content that has no business living in a coordination row.
  • ~/.claude/projects/-<slug>/memory/: per-machine, per-project Claude Code state. Wiped by reset, lost on machine failure, not synced across operator workstations, not visible to teammates or to fleet agents on other machines. Holds feedback_*.md, user_*.md, reference_*.md — the most reusable, most durable category of accumulated knowledge.
  • ~/.claude/CLAUDE.md: operator-local global instructions file. Loaded by every Claude session on that one machine — invisible to every other operator, agent, machine, and to the agents the fleet spawns into worktrees of gyrum repos.
  • ~/.claude/agents/ (subagent definitions), ~/.claude/settings.json, ~/.claude/skills/: same shape — operator-local runtime config for capabilities the fleet expects to be uniformly available.

Each substrate is fragile in a different way. Each is invisible to most of the audience the content is meant to govern. And each becomes the de facto archive of decisions that should have been recorded somewhere durable, reviewable, and shared — i.e., in git.

Cross-references for the same shape:

  • warp#1004 flagged Claude per-project memory's fragility (worktrees + machine reset + cross-workstation drift).
  • warp#1008 caught me proposing to add a fleet-wide regression-fixture rule to ~/.claude/CLAUDE.md — operator-local — when it's a fleet contract.
  • The pattern recurs every time an agent or operator drafts a rich warp ticket body, then the ticket gets marked done and the design rationale only exists as a Postgres row.

Decision

Durable engineering content lives in git. Ephemeral coordination and runtime state can live in ephemeral substrates.

Concretely:

Content type Goes in Substrate
Design rationale, ADRs, decisions <repo>/docs/decisions/ADR-NNN.md git
Fleet-wide rules and conventions gyrum-labs/devtools/templates/... (regenerated into every repo's CLAUDE.md) git
feedback_* / user_* / reference_* memories <repo>/.claude/memory/ git
Subagent definitions, skills, settings the fleet relies on <repo>/.claude/agents/, <repo>/.claude/skills/, <repo>/.claude/settings.json git
Coordination metadata (claim status, heartbeats, lease) warp tickets Postgres (ephemeral OK)
Active session state, in-flight project_* memories ~/.claude/projects/-<slug>/memory/ per-machine ephemeral
Operator personal preferences (terse responses etc.) ~/.claude/CLAUDE.md per-machine ephemeral

The substrate test: if this content disappeared overnight, would it need to be reconstructed? Reconstructable / time-decaying / single-machine state can be ephemeral. Reconstruction-impossible engineering content (decisions, rationale, accumulated rules, durable agent capabilities) goes in git.

Warp tickets stay valuable for what they're actually good at: coordinating who's working on what, claim/heartbeat/release flow, queue-of-work-for-agents. When a warp ticket grows into a design doc, the design content moves to an ADR or <repo>/docs/proposals/<slug>.md, and the warp ticket becomes a thin pointer with the coordination metadata. Same shape for .claude/ — when content is fleet- or repo-relevant, it lives in <repo>/.claude/; when it's truly per-machine session state, it stays in ~/.claude/.

Consequences

Easier

  • A git clone on any machine — operator's, teammate's, CI runner's — yields the full design history, accumulated memories, agent capabilities, and conventions the repo expects. No manual sync, no per-machine setup.
  • Worktrees of a repo (the gyrum-start-work and gyrum-workspace paths) inherit memory, ADRs, agent defs naturally because they share the working tree. The "agents in worktrees can't see memory" failure mode dissolves entirely (this also closes warp#1004's scope).
  • Memory and rule changes get reviewed in PRs. An agent that writes a wrong feedback memory or an outdated rule is caught at PR time, not after the bad memory has propagated.
  • Decisions become greppable from the codebase, not from a separate ticket UI. git blame works on rules and memories.
  • Fleet contracts (e.g. the regression-fixture rule from warp#1008) live in the gyrum CLAUDE.md template and propagate via gyrum-setup --sync-claudemd — visible to every operator, agent, machine, and Claude session in any gyrum repo.

Harder

  • Agents writing memories during a session must commit them via the gyrum review workflow rather than just dropping a file in ~/.claude/projects/.... This is a workflow shift; mitigated by gyrum-bind-memory (a symlink helper from ~/.claude/projects/-<slug>/memory/ to <repo>/.claude/memory/ so existing tooling still works at the path it expects).
  • Memory category sort: feedback_* / user_* / reference_* go to in-repo durable; project_* (in-flight, time-decaying) stay ephemeral. Misclassification puts churn into git history. Mitigated by a <repo>/.claude/memory/README.md convention doc + a CI check that flags project_* files committed to the durable dir.
  • Secret leakage risk: an agent could write a memory containing a token, push it, leak. Mitigated by a pre-commit secret-scan hook on <repo>/.claude/memory/.
  • Stale references: a memory referring to a function or file that's since been removed becomes a misleading source of truth. Mitigated by a CI check that resolves greppable identifiers in memory against the current tree (with an explicit historical: opt-out for legitimate retrospective entries).
  • Multi-repo migration: existing per-Claude-project memories on each operator's machine need to be migrated into the appropriate gyrum repos' .claude/memory/ dirs, with PRs per repo for review. One-time cost; covered by a gyrum-migrate-memory one-shot.

What we sign up to operate

  • A pre-commit secret-scan hook on <repo>/.claude/memory/ (and <repo>/.claude/agents/ etc.).
  • A CI check for stale identifier references in memories.
  • A periodic curation pass — same shape as ADR / debt log curation, kicks in when <repo>/.claude/memory/ grows past N entries.
  • The convention doc itself (<repo>/.claude/memory/README.md) — what goes here, what doesn't.
  • gyrum-bind-memory and gyrum-migrate-memory as supported devtools.

Alternatives considered

  • Symlink the worktree memory dir to the source-project's ~/.claude/projects/... memory dir (the original warp#1004 framing). Fixes the worktree symptom without addressing the substrate's fragility. If ~/.claude/projects/... is wiped, the symlink resolves to an empty dir silently. Ruled out: band-aid on a fragile substrate.

  • Keep design content in warp ticket bodies, accept the DB-wipe risk. Warp is "production enough" today, and any DB wipe would be operator-noticed. Counter-argument: the visibility problem still bites — design rationale in a Postgres row isn't greppable from the codebase, isn't reviewable in PRs, doesn't show up in git log, doesn't follow the operator across machines, and isn't visible to agents that aren't running with warp credentials. Ruled out: even if DB durability is acceptable, the discoverability shape isn't.

  • A separate "fleet wiki" service outside both warp and git. Adds a third substrate to maintain. Doesn't compose with the gyrum review workflow — a fleet wiki edit doesn't go through gyrum-review-pr and doesn't show up in branch diffs. Ruled out: more substrates ≠ better.

  • Fleet-wide content in operator-local ~/.claude/CLAUDE.md. Visible to one operator on one machine; invisible to every agent and teammate. Ruled out by definition for fleet-wide content.

  • A hybrid where rich design content goes in git but warp tickets duplicate the body for "convenience". Predictable drift between the two copies; the warp copy ages stale because nobody updates it after the ADR moves. Ruled out: single source of truth.

Migration

The substrate-shift is bigger than a single PR. Stage as a sequence of small migrations, each closing a specific bit of fragility:

  1. This ADR lands first. Establishes the principle. Subsequent migrations cite it.
  2. gyrum-bind-memory + gyrum-migrate-memory ship as devtools (supersedes the original warp#1004 symlink-the-worktree shape).
  3. Per-gyrum-repo migration PRs for accumulated feedback_* / user_* / reference_* memories. Reviewed per repo.
  4. Regression-fixture gate (warp#1008) goes into gyrum-review-pr + the gyrum CLAUDE.md template, NOT into ~/.claude/CLAUDE.md.
  5. <repo>/.claude/agents/ and <repo>/.claude/skills/ become the canonical home for fleet-relied-on agent defs and skills. ~/.claude/agents/ retains operator-experimental ones.
  6. Pre-commit secret-scan hook + stale-reference CI check.
  7. Audit pass: warp tickets with rich body content get migrated to ADRs / proposals; the warp ticket body shrinks to a thin pointer.