Skip to content

ADR-016: Vault Organization and Source-of-Truth Chain

Status

Accepted (2026-05-03). Implemented across commits eff820e12, 5ae863778, ae9c4cf8e, d54313f13, 3d9aa508a plus ralph repo 0.2.0.

Context

Before this restructure the repo had four parallel doc trees rooted at the top level:

/docs-code/      Obsidian vault for codebase knowledge (architecture, domains, infrastructure)
/docs-staff/     mkdocs source for internal staff docs
/docs-public/    mkdocs source for customer-facing help
/docs/           ad-hoc bucket: ADRs, conversations, plans, features, marketing, benchmarks, cto, ...
/specs/          design intent for in-flight features (ralph workspace)

This shape evolved organically, not by design. Three problems compounded:

  1. Audience mixing in folder names. docs-code/ → developer, docs-staff/ → internal, docs-public/ → customer. The docs- prefix repeated three times across top-level dirs gave them visual weight equal to apps/ and packages/, while their actual relationship was "different audiences for the same kind of content."

  2. Source-of-truth was implicit. When code disagreed with docs-staff/docs/lockouts/index.md (the business rules), the doc was canonical. When code disagreed with docs-code/domains/checkout.md (the architecture wiki), the code was canonical. Nothing in the layout signaled this. Agents had to infer authority from prose.

  3. Agent-first navigation was a side effect. Every artifact was for both humans and agents, but the org-chart split (staff/public/dev) optimized for human audiences. Agents reading the vault have to grep across three trees to answer "what's the canonical source for X?"

Metrognome's product-development model is also unusual: AI agents do most of the implementation, with Aaron acting as architect and reviewer. That makes the docs more load-bearing than typical:

  • Specs are first-class permanent docs. They get written before implementation, ratchet through review, and stay updated as intent changes. They are not transient PM artifacts.
  • Business rules drive code. A refund policy isn't documentation that follows the code; it's the constraint the code must satisfy.
  • Agents need predictable file paths. Glob-driven schema (features/*/spec.md) beats prose discovery every time.

The restructure had to express these choices in folder shape, not just prose.

Decision

Single vault rooted at /docs/

The whole /docs/ tree is one Obsidian vault (.obsidian/ at the root, not nested inside a subdir). Agents and Aaron share this vault. Whole-tree grep is the navigation primitive; per-subtree READMEs are the hubs.

docs/
  .obsidian/
  README.md                       vault entry, authority chain, layout map
  business/                       immutable product rules               (staff site)
  ops/                            staff procedures, admin runbooks      (staff site)
  help/                           customer-facing reference             (public site)
  marketing/                      brand, ICP, positioning, KPIs
  features/                       design intent + plans + ralph artifacts
  decisions/                      ADRs (this file)
  engineering/                    architecture, domains, infrastructure, agent tooling
sites/                            mkdocs publish wrappers (outside docs/ — see below)
  staff/
  public/

Audience-specific containers (code/, staff/, public/) are gone. Subtrees are organized by purpose (business rules vs procedures vs help vs ad strategy vs architecture), not by audience. Agents subsume all human audiences anyway — they read everything.

Source-of-truth chain

The folder structure encodes a hierarchy. Higher overrides lower; drift downstream is a bug to fix.

  1. Business logic (business/) — immutable rules about how the product works. Code conforms. When code drifts from a rule, the code is wrong; never silently update the rule.
  2. Spec (features/<name>/spec.md) — canonical design intent for an implementation. Bridges business to code. Updated when intent changes; specs are not ephemeral.
  3. Code — the running thing. Subordinate to spec, which is subordinate to business logic.

Every domain has a parallel chain across these subtrees: business/checkout/index.mdfeatures/<checkout-feature>/spec.mdengineering/domains/checkout.md → actual code.

Audience hierarchy

Audience-narrower subsets are projections of agent-wide content, not separate corpora.

  • Agent docs = the entire vault. Agents read everything.
  • Staff docs = business/ + ops/ published as docs.metrognome.com via sites/staff/.
  • Public docs = help/ published as help.metrognome.com via sites/public/.

mkdocs sites are thin publication wrappers, not content silos. Each sites/<site>/mkdocs.yml uses docs_dir: ../../docs and an explicit nav: listing the published pages. Vault-only content (engineering/, features/, marketing/, decisions/) is not in any nav.

sites/ lives outside docs/

mkdocs forbids site_dir inside docs_dir. With docs_dir: docs/, no Vercel outputDirectory configuration can satisfy both mkdocs and Vercel if the wrapper is nested inside docs/. So mkdocs wrappers live at repo root in /sites/staff/ and /sites/public/.

Folder-per-feature with role-named files

Inside features/, every feature is a folder. Files inside are named by role, not by feature.

features/<name>/
  spec.md              canonical design intent  (the source of truth)
  plan.md              implementation plan with **Status:** + checkboxes (ralph drives this)
  prompt.md            ralph protocol for this divergence
  progress.txt         cross-iteration learnings (auto-grown by ralph)
  research.md          background, when needed
  runbook.md           ops procedure when applicable
  retro.md             post-mortem when applicable
  deploy-checklist.md  one-time deploy artifacts

Strict folder-always — even single-file features get a folder. Two reasons:

  • Glob discoverability. **/spec.md returns every design intent in the vault. **/plan.md returns every implementation plan. Cross-feature reasoning becomes one glob.
  • Self-describing filenames. Read('features/posthog/spec.md') is unambiguous in a tool result. Read('features/posthog/index.md') requires folder context to interpret. Removing prefix-parsing from filenames lowers cognitive load per file.

Subject lives in the folder name, role lives in the filename — same convention as everywhere else in the vault (business/lockouts/index.md, engineering/architecture/api/overview.md).

Ralph integrated to the convention

Ralph (aaron-hogan/ralph 0.2.0) was updated to look for files at docs/features/<name>/{spec,plan,prompt,progress.txt} instead of the prior docs/specs/<name>-{plan,prompt,progress}.md pattern.

Consequences

What gets easier

  • Agent navigationdocs/features/<name>/spec.md is always where the canonical intent lives. No prefix to parse, no audience folder to choose between. Glob **/{spec,plan,runbook,retro}.md finds everything by role.
  • Authority disputes — when something is wrong, the chain (business > spec > code) tells you which side moves. ADR-016 (this doc) names the rule.
  • mkdocs sites stay thin — adding a new published page is a single nav entry. Reorganizing vault content doesn't require restructuring published sites.
  • Ralph fits cleanly — the <name>/{spec,plan,prompt}.md shape matches how ralph thinks about a feature.

What gets harder

  • mkdocs docs_dir: ../../docs is non-standard. New contributors need the comment in mkdocs.yml to understand the shape.
  • Vercel root directory for docs-staff and docs-public projects had to be repointed once at restructure time. Future site configuration changes happen in two places (vercel.json + mkdocs.yml) instead of one.
  • Strict folder-always means even a one-file feature creates a folder. Minor but adds an extra mkdir.

What this rules out

  • Specs as ephemeral PM artifacts. They stay in features/ after shipping and get updated as intent changes. We don't have a separate "PRD" concept; spec is the canonical artifact.
  • Per-audience doc trees at top level. No reverting to /docs-staff/ or /internal/ style. Audience is a projection (mkdocs nav), not a layout.
  • Skill-as-source-of-truth. Skills like mg-checkout were a stand-in for vault content that didn't exist yet. With domain pages in place under engineering/domains/, the skill content gets folded back in and the skills retire (Phase 2).