All skills
Architecture

canonical-model

Writes down how your whole system works, and how it should, in one place.

Use it for

Projects too big for anyone to hold in their head.

Not for

Small scripts and one-offs.

The code is evidence. The current model is the best explanation of that evidence. The goal model is the system being built. Development is reconciling the three: code ↔ current → goal.

A canonical model is not a plan, a summary or a README. Reading it alone must give a full understanding of the existing or intended system: where everything comes from, how it is processed and stored, how it is served, what it costs, what breaks, and what users see. Compress away implementation noise and keep the causal structure. If the last incident could not have been predicted from the model, the model is not deep enough.

Contents

Current and goal use the same section numbers, so each section can be compared side by side. Scale the sections to the project; coverage matters more than section count.

  • 00 system: layers, organizing rules, machines, stores, identities, the truth order (which source wins), dependency direction.
  • Domain sections, one per domain: for each fact, source → transform (path:function) → store → publication → surface that shows it. Name where truth lives and what is derived from what.
  • Surfaces: everything served and shown, including pages, tools, APIs, CLIs, bots and jobs, with the data each reads. The model covers the frontend and features, not only the backend.
  • Runtime: for every request path, the calls, rows and bytes it uses against each quota; whether it is cached; whether cost grows with traffic, table size or the product of two tables; what happens when a dependency or quota runs out; abuse protection on reads and writes; privacy, retention and backups.
  • Operations: workflows, schedules, runners, secrets, deploy, rollback.
  • Code: repo layout, languages, conventions, module boundaries.
  • Design (if there is a UI): the design thesis (who it is for, how it should feel, its signature), then tokens, components, templates.
  • Invariants and contracts: what must always hold, and what enforces it.
  • Problems (current only): everything wrong, slow, duplicated or contradictory, each with evidence.
  • Goal only: resolutions (one per current problem), migration (ordered phases, each with an exit check), how the team works.
  • Glossary: one name per concept in every layer. See "Sharpen language".
  • Decisions: short records for choices that are hard to reverse, surprising later, and a real trade-off. Skip the rest.

Layout

Default to docs/model/. Keep an existing model's names (for example v1/ and v2/) instead of renaming.

docs/model/
  README.md            index table (section, what it covers, current link, goal link) and notation
  STATUS.md            inspected commit, date, what was verified and how, what is pending;
                       committed, built, deployed and observed are separate states
  SUMMARY-current.md   the whole current system on one page
  SUMMARY-goal.md      the whole goal system on one page
  GLOSSARY.md
  current/NN-name.md
  goal/NN-name.md
  decisions/NNNN-slug.md

The repo copy is authoritative. Any other copy (Desktop, wiki) is generated from it. Point AGENTS.md / CLAUDE.md at docs/model/.

Rules for every claim

  • Derive from code and observed behavior, not from docs, comments, commit messages or what anyone said. When the user states how something works, check the code and surface a contradiction immediately.
  • Label each claim measured, inferred or guess, and cite path:function, a command or its output.
  • Keep uncertainty explicit. Record exceptions and unknowns instead of drawing a clean architecture that does not exist: Project.owner: Organization (high confidence, measured in src/projects/model.ts:owner) Exception: legacy personal projects. Unknown: whether they can still be created.
  • Treat contradictions as findings. Say which it is: the model is wrong, the code is wrong, there are two systems, or a transition is half done.
  • Derive costs from code. Run every SQL statement through the query planner, map every client fetch to the pages that call it, read the cache headers, and compute headroom per quota. Check platform limits and billing in the provider's docs before using them in a budget.

Modes

Build

  1. Inventory everything that exists: routes, pages, tools, endpoints, jobs and workflows, tables and stores, external sources and services, scripts. This list is the coverage checklist. For a large repo, give one read-only subagent each domain on a cheap model, and do the synthesis yourself.
  2. Trace each item end to end and write the sections.
  3. Check coverage. Every inventory item appears in the model. Add whatever is missing; never leave it out quietly.
  4. Write the problems with evidence.
  5. Teach-back test. From the model alone, answer three concrete questions (where does X come from, what happens when Y runs out, what does page Z cost), then check each answer against the code. A wrong answer means the model is too shallow. Deepen it and repeat.
  6. Write the summaries and STATUS last.

Goal

  • Start from the current problems, measured pain, and the real cadence and scale. Do not start from an ideal architecture. Each new component names the measured problem it fixes and why the cheapest fix within the current stack is not enough.
  • Aim for smart, simple and easy to extend, good enough for real use, not perfect. Mark aims as aims.
  • One canonical source, many projections. The same model and data serve runtime behavior, documentation, wikis and generated tables. Projections are generated, never hand-copied. No competing schemas, duplicate parsers or docs-only copies of rules.
  • Every current problem gets a resolution. The migration is ordered, and each phase ends in a check.

Update

Any change that alters a flow, store, cost, surface, invariant or term updates the model in the same commit, plus STATUS. Code and model disagreeing is a bug in one of them.

Audit

Compare the model against the current code for drift. Then report strengths, weaknesses, opportunities and coverage gaps, and say what the most likely next incident is and whether the model would have predicted it.

Sharpen language (during any conversation)

  • When the user uses a term that conflicts with the glossary, say so: "The glossary defines cancellation as X, but you seem to mean Y. Which is it?"
  • When a term is vague or overloaded, propose a precise one: "By 'account', do you mean Customer or User?"
  • Stress-test relationships with concrete edge-case scenarios.
  • When a term is settled, update GLOSSARY.md right away. It holds project-specific terms only, with no implementation details: **Invoice**: a request for payment sent to a customer after delivery. _Avoid_: bill, payment request
  • A decision record has five parts: title, context, decision, alternatives rejected and why, consequences.

Using the model

Before a significant change, read the summaries and the affected sections, then ask:

  • Does this move the current model toward the goal?
  • Does it add a concept we will later have to remove?
  • Does it break an invariant?
  • Is it fixing the structure or patching a symptom?

Glossary and decision-record practice adapted from Matt Pocock's domain-modeling (MIT).