Architecture
The one-page orientation map — how the codebase is organised, what each layer owns, and where every detailed rule lives
How to read this page
This page is the map, not the law. It says how the codebase is organised and points to the page that owns each rule; it deliberately restates none of them, so no rule ever has two homes. The why behind the whole shape is the architecture decision record; start there when you need the reasoning or the rulings.
In plain terms: this is the front index of the precedent bank. Each entry tells you which binder holds the full clause; the clause itself is never photocopied onto the index.
The three responsibility areas
packages/domain/ what things ARE — canonical types, rules, projections
↑
├──────── convex/ where things LIVE — storage, authority, transactions
└──────── src/ how things are SHOWN and OPERATED — the FSD frontendpackages/domain/is the one official rulebook: runtime-free TypeScript stating what Documents, Modules, Blocks, Fields, Mentions and Proposed Changes are and how they behave. Both the backend and the frontend import it, so the two can never disagree about meaning. It has no UI code and no database code, and it is tested line-for-line.convex/is the vault: the assembled schema, every registered backend function, authority checks, and transactions — organised by business concept, never by technical bucket. Rules for working here: Convex.src/is the workbench: the React frontend, organised by Feature-Sliced Design (FSD) — a documented industry filing system (fsd.how), chosen over a homemade layout precisely so that any developer or agent can look the rules up.
In plain terms: one dictionary of defined terms (packages/domain), one
registry that stores and guards the records (convex), one office where the
work is done (src). The dictionary answers to no one; the other two answer
to the dictionary.
The FSD layers, outer to inner
Higher layers may import lower ones; never the reverse. Every slice exposes a
deliberate public door (index.ts); reaching past a door is a violation, and
the Steiger gate enforces this mechanically.
| Layer | Owns | Example |
|---|---|---|
src/app/ | Composition root: routing, providers, global CSS | /app/canvas/$routeKey |
src/pages/ | Screen-level composition, one slice per screen | document-work/, folio-container/, library-space/ |
src/widgets/ | Substantial composed surfaces | canvas-shell/, drafting-editor/, story-block/ |
src/features/ | One user intention offered from two or more surfaces | duplicate-folio/, share-folio/, search-libraries/ |
src/entities/ | The business nouns' UI and client logic | folio/, block/, module/, document/, agent/ |
src/shared/ | Domain-neutral bricks — no product nouns allowed | ui/atoms/, ui/molecules/stack/, lib/ |
Notes that trip newcomers:
- A
features/slice is earned, not planned. The first wasfeatures/duplicate-folio(2026-09-07, GB-151), which owns the frontend execution of Duplicate — the executor binding, a pending guard per source, one failure voice — because two Pages offered it. The same test has since admittedpublish-to-library,review-folio-proposal,search-libraries,share-folioanduse-library-master(manage-librarywent back to its one remaining Page on 2026-10-01, when the listed Library card lost its menu). A Feature is earned by a second surface, never by several buttons on one canvas; every other intention stays a Page adapter until then (the 2026-08-27 placement ruling in the architecture decision). Cards and menus receive callbacks and never import a Feature; session resolution and navigation stay with the Page. - Atomic Design survives only inside
shared/ui/(atoms/,molecules/); the global organisms/templates levels are retired. - The Stack is furniture, not a domain object. A Block is rendered with a Stack; a Block is not a Stack. Product nouns are banned from Shared UI. Full rules: The Stack.
- Cross-entity presentation goes through FSD's
@x/convention, never a sidestep import.
Where components live, the upstream shadcn landing zone, and promotion rules:
UI Components. Versioned directories
(v1/, v2/ …): Codebase Versioning.
Dependency direction
app → pages → widgets → entities → shared
↓ ↓ ↓
packages/domain ← convex (via the generated API only)Frontend runtime code talks to the backend only through Convex's generated API — never by importing backend implementation files. Both humans and Goldie are peer operators of the same canonical operations: one implementation per business action, whatever triggers it (menu, drag, keyboard, or agent tool).
Where the rest of the law lives
| Concern | Owning page |
|---|---|
| Why this architecture; the drift-prevention rules; domain chapter list | Architecture decision record |
| Backend functions, schema, authority | Convex |
| Component homes, landing zone, imports | UI Components |
| Card furniture: regions, parts, where the stories are | The Stack |
| Stories and their gates | Storybook |
| Styling and Counsel tokens | Tailwind |
| File-size trigger and deepening | File Size |
| Directory versioning | Codebase Versioning |
| Commands, gates, git rules | Repository Operations |
| Planning and task authority | Planning Workflow |
Historical depth — the migration ledger and the original grill that settled
this shape — lives in the archived plan 2026-08-09-codebase-architecture.
It is history, not law: where it and the pages above disagree, the pages win.