Skip to content

Documentation Hierarchy — Source of Truth Spec

Documentation Hierarchy — Source of Truth Spec

Created: 2026-05-16 after audit revealed 6 out-of-sync documents tracking overlapping state. Enforced by: Every session start must read HANDOVER + LEDGER before touching anything.


The Problem This Solves

Docs kept diverging because status information lived in 4+ places with no ownership rule. When a phase completed, we’d update the plan doc but not LEDGER, or update HANDOVER but not AGENTS.md. Two sessions later, a stale reference would surface and cause confusion.

Root cause: No rule about which file owns which information.


The Hierarchy

TIER 1 — Source of truth (each owns its information type, nothing else does)

FileOwnsDoes NOT own
LEDGER.mdProject status. What phase are we on? What’s done? What’s blocked?Not for ops state, not for step-by-step how-to
HANDOVER.mdOperational state right now. Current branch, current goal, next action, active blockers. Auto-written by session hooks.Not for project history, not for how-to
AGENTS.mdAgent runtime context. MCP architecture, tool selection rules, session protocol, branch naming.Not for project status, not for operational state
docs/plans/*.mdExecution recipes. How to execute a project — step by step, with rollback. Written ONCE before execution starts.Not for status tracking. Never updated once execution begins.
docs/decisions/ADR-*.mdArchitecture decisions. Context, options considered, decision, consequences.Immutable after Accepted.
.mcp.jsonWhat MCP servers are configured for this repo.AGENTS.md table is derived from this — .mcp.json wins on conflict.

TIER 2 — Derived (read-only references to Tier 1, never the source)

FileDerives from
~/.claude/CLAUDE.md (global)MCP server table derives from .mcp.json. Update .mcp.json first, then sync global CLAUDE.md.
AGENTS.md MCP tableDerives from .mcp.json. AGENTS.md is updated when .mcp.json changes — not independently.
Plan doc status headerDerives from LEDGER. The header is a convenience pointer; LEDGER is the source.

The ONE Rule That Prevents Stale Docs

Phase status lives in LEDGER only.

When a phase completes:

  1. Update LEDGER.md row status. That’s it.
  2. Do NOT also update the plan doc, HANDOVER, AGENTS.md, CLAUDE.md.
  3. Optionally update the plan doc’s status header line as a pointer: > **Status:** ✅ Phase X complete — see LEDGER for current status

When an operation completes (not a project phase, but a one-off action):

  1. Update HANDOVER.md next-action and major-state sections.
  2. If it affects a project, also update LEDGER.md row.

When MCP config changes:

  1. Update .mcp.json first (canonical).
  2. Update AGENTS.md MCP table to match.
  3. Update ~/.claude/CLAUDE.md MCP table if it’s a global server.
  4. Never add an MCP server to AGENTS.md or CLAUDE.md without first adding it to .mcp.json.

Plan Docs Are Write-Once (Like ADRs)

Once a plan doc is written and execution starts, it becomes a reference document, not a status tracker.

Good plan doc update: Fixing a factual error in the execution steps (e.g., wrong API endpoint). Bad plan doc update: Adding to completed steps, or updating “Status: In progress”.

Status goes in LEDGER. The plan doc is the recipe; LEDGER is the cook’s journal.

Exception: The status header line at the top of each plan doc can be updated to a one-line summary pointing to LEDGER (e.g., > **Status:** ✅ Complete — see LEDGER). This is a pointer, not the status itself.


Session Start Checklist (in order)

  1. Read HANDOVER.md — what’s the current operational state?
  2. Read LEDGER.md — what projects are active, what’s blocked?
  3. Read AGENTS.md — what MCP tools are available, what are the session rules?
  4. Read plan doc only if actively executing that plan.
  5. Do NOT read plan docs to determine status — LEDGER is the source.

What Gets Updated When (quick reference)

EventUpdate these files
Phase completesLEDGER (project row status)
Operational state changes (cron disabled, worker deployed, connection added)HANDOVER (next-action + major-state)
New MCP server added.mcp.json → AGENTS.md → ~/.claude/CLAUDE.md
New project startsLEDGER (new row) + new plan doc in docs/plans/
Project completesLEDGER (move to Completed) + plan doc status header update
Architecture decision madeNew ADR in docs/decisions/
V5 invariant changes.claude/rules/v5-invariants.md + master plan (both in same commit)

Archiving Plan Docs

When a project moves to “Completed” in LEDGER:

  • Update the plan doc’s status header to > **Status:** ✅ COMPLETE — closed YYYY-MM-DD
  • Do NOT move the file. Leave it in docs/plans/ as a historical reference.
  • If the doc has conflict markers or is genuinely broken: fix or move to docs/archive/plans/.

Anti-Patterns (don’t do these)

  • ❌ Updating a plan doc with ✅ Phase 3 done — use LEDGER
  • ❌ Updating HANDOVER.md with project phase progress — use LEDGER
  • ❌ Adding an MCP server to AGENTS.md without updating .mcp.json
  • ❌ Updating ~/.claude/CLAUDE.md MCP table before .mcp.json
  • ❌ Using a plan doc to determine “what’s done” — use LEDGER
  • ❌ Updating LEDGER without updating HANDOVER when the next-action changes
  • ❌ Having a “sources of truth” in two places for the same information type