The step before every page-class law: a five-step derivation from the domain to a class — or to no page at all — and an index that translates the industry's skeleton names into this repo's classes.
The law before the page-class laws. Every one of those owns a page SHAPE and pins the kit that enforces it; this one owns the step that comes first — how a business function becomes a page class, or no page at all. It has no kit, because the thing it governs is a decision rather than a component: its enforcement is the Page-type laws table in ../AGENTS.md, which only accepts a class this derivation produces. The living example is the tasks app (/labs/apps/tasks), where one domain object wears ten expressions and every one of them is a view of the same screen.
01
Page form is derived, never chosen. A page is the last output of a chain that starts with the domain — what objects, relationships, events and rules actually exist — passes through the ONE task the reader came to do, names the information structure that task needs, and only then picks the expression that answers the task's question with the fewest mental conversions. Skipping to the end is how a repo ends up with a table because an API returned an array, and a detail page because the route carried an id.
The corollary is that the same object has no single page. An issue is a collection while it is being queried, a status flow while it is being processed, a thread while it is being discussed, and an event sequence while it is being audited. Those are four expressions of one object, and the question "which page is the issue page" has no answer — only "which task".
02
Five steps, each with one question and one output. The output of each is the only input the next one may use.
| Step | Question | Output |
|---|---|---|
| 0 · Gate | Does this need a page at all? | A page, or a slot inside one that exists |
| 1 · Task | What is the reader here to DO — one verb? | An intent: find, understand, compare, advance, or trace |
| 2 · Model | What information structure does that verb need? | Collection, hierarchy, relationship, object, process state, event sequence, document, diff, metrics, space, or time |
| 3 · Expression | Which rendering answers the question directly? | A page class from the table in AGENTS.md — or a gap, recorded |
| 4 · Context | Where does the supporting content live? | Split, inline, its own page, or a dialog |
A page whose class cannot be named by walking these steps is not "special". It is either a class that already exists wearing an unfamiliar body, or a class this repo has not legislated — and the second case is handled by the growth law at the end of this document, never by building first.
03
A page is expensive: it takes a route, a nav entry, a heading, a place in the composition pyramid, and it promises to be there when the URL is opened tomorrow. It earns that cost on three grounds, and only these three:
<PageContainer fluid> exists for exactly this).Everything else folds into a surface that already exists: a tab on a family page, a view of an app screen, a DetailPeek beside the list, a feature card on a dashboard, a dialog. The tasks app's ten views share ONE nav entry for this reason — none of them is a page in its own right, each is an expression of the screen.
04
The reader's intent is a verb, and each verb pulls a small set of information models. Naming the verb first is what stops a page from serving two of them at once — a table that is also trying to be a status board is the most common result of skipping this step.
| Intent | The reader is trying to… | Information models it pulls |
|---|---|---|
| Find | locate one thing among many | Collection, hierarchy, space |
| Understand | judge one thing's identity and state | Single object, relationship, document |
| Compare | weigh things against each other | Collection (field by field), diff, metrics |
| Advance | move work to its next state | Queue, process state, rules and configuration |
| Trace | reconstruct what happened, and who said what | Event sequence, discussion |
A page has ONE primary intent. Secondary intents are served by supporting models that step 4 places — the deal page's activity timeline is a Trace model supporting an Understand page, and it lives in a feature card, not in a second heading.
05
The information model and the question it must answer pick the expression, and each expression maps onto a page class this repo has legislated — or onto a recorded gap. The index below is the vocabulary a request arrives in (the industry's skeleton names) translated into this repo's classes. Several skeletons collapse into one class with a different BODY: table, catalog and card grid are all the data view with a different DataViewBody. That is the kit-over-convention design paying off, and it is why the class count stays small while the skeleton count does not.
| Skeleton (as requested) | The question it answers | Class here | Status |
|---|---|---|---|
| Dashboard / overview | Trend, distribution, anomaly | Dashboard | Kit, law, /labs/layout/dashboard |
| Comparison table | Field-by-field comparison across rows | Data view, table body | Kit, law, /labs/layout/table |
| Catalog / discovery | Identify and enter a resource; search plus facets | Data view, card-list body | Kit, law, /labs/card-list/* |
| Card grid | The image is the recognition anchor | Data view, grid body | Kit, law, /labs/layout/list |
| Sectioned detail | Object identity and current state | Detail | Kit, law, /labs/apps/crm/deals/[id] |
| Master-detail workspace / queue | Which is the one; then the next one | Detail, the peek | PeekLayout + pager cover triage, and the flat queue exists as an agenda (/labs/apps/crm/activities, tasks my-work); Support adds the queue-first conversation workspace (/labs/apps/support/inbox), with URL-selected tickets and the same conversation body as its detail page |
| Kanban / swimlanes | Stage flow where moving IS the action | Kanban | Kit, law, /labs/layout/kanban |
| Configuration form | One record being created or edited | Form | Kit, law, /labs/layout/form |
| Control panel | Many small independent decisions | Settings | Kit, law, /labs/layout/settings |
| Parameter studio | Tune a live preview | Studio | Kit (src/components/studio/), law in the kit's JSDoc |
| Full-screen tool | Manipulate one artifact with every pixel | Workbench | Law, no kit until the second instance |
| Step wizard | Stage and next step, with input at each | Form, staged | Not legislated — a section of the form law once a first instance exists, not a class |
| Status wall | Is everything up; what changed since | Dashboard, status tiles | Tiles and tones exist; the event list under them has no kit |
| Event timeline / activity feed | What already happened, in order | A BODY, not a class — inside a Detail card, a data view, or a plain page | Kit src/components/timeline/ (law in its file-top JSDoc; rail and feed shapes); deal Activity, task History, tasks activity, /changelog |
| Trace drill-down | What happened at each stage of one run | — | Not legislated; the page-level cousin of the Runs diagram family |
| Hierarchy tree | Parent-child, or a path | — | Not legislated; tree is also a pending picker anatomy |
| Relationship list | Dependency or impact, as adjacency lists | — | Not legislated |
| Discussion thread | Who said what, and how it was answered | A BODY inside a queue workspace or Detail | First instance: Support conversation (/labs/apps/support/inbox); public replies and private notes, shared with /labs/apps/support/[key] |
| Continuous document | Read top to bottom, jump by section | — | One instance (/labs/codex/[slug]: article plus section rail); legislate on the second |
| Revision comparison | What changed, before against after | A BODY inside Detail | Content Revisions compares saved snapshots as a unified line diff; a standalone side-by-side comparison page is not legislated |
| Calendar / scheduling | When is it occupied, when does it conflict | — | One instance (/labs/apps/calendar); legislate on the second |
| Map / spatial canvas | Location, boundary, distribution | Not a page class by design | A tab family at /labs/effects/map — each map is a live WebGL context, so the family keeps exactly one mounted |
Two readings of the index are wrong. It is not a list of what this repo does and what it does not: every "not legislated" row is a shape that has not yet appeared twice, and the growth law below says what happens when it does. And it is not a menu — a request arrives with a skeleton name because that is the vocabulary people have, but the row is only confirmed once steps 0–2 land on it.
06
Every page has content that is not its primary model — the record beside the list, the raw log under the summary, the confirmation before the delete. The surface it lives on is decided by how the reader moves between it and the primary content, and the ladder has four rungs. The rule from detail.md generalises: the cut between rungs is width and frequency, never importance.
| The reader… | Surface | Here |
|---|---|---|
| Switches object repeatedly, judging each against the list | Split — the supporting panel beside the primary, both visible | PeekLayout + DetailPeek; the tasks board's peek |
| Needs it briefly, then goes back to where they were | Inline expansion, or a Sheet that returns them to the same scroll | Collapsible sections; the settings drawer |
| Will share it, or needs the whole width to read it | Its own page — back through step 0 | /list/[id] from a peek's href |
| Confirms or supplies one or two values | Dialog, and for a single destructive step, AlertDialog | The form law's footer gates; the delete confirms |
The failure this step exists to prevent has one shape: a Drawer over the queue the reader is triaging. It hides the very list the judgement depends on, and every close is a re-orientation. The detail law's two-surface split was legislated out of that exact rework.
07
The tasks app is this law's living example because it refuses the question "what does the task page look like". A task is a collection in list and table, a status flow in board, a plan in timeline and calendar, a priority field in matrix, an event sequence in activity, a set of figures in overview and workload, and a queue in my-work — ten views, one object, one nav entry, one [key] detail page they all open into.
The Support app (/labs/apps/support) proves the same rule for a conversation-led object: Inbox reads the thread, Queue assigns ownership, SLA orders obligations, Customers groups relationship history, and Analytics aggregates the same events. Replying, adding a note, assigning and resolving each append to one log; every view opens the same [key] detail. It composes the data-view, detail, timeline and dashboard kits, with StatTile, ChartContainer, ShareBar and RankBar as its analytical parts.
The Content app (/labs/apps/content) follows a revision through Library (data view), Workflow (kanban), Calendar (date-picked weekly agenda), Channels (live edition catalog) and Analytics (dashboard). All five open one [key] editor, composed from the form and detail kits. The calendar is an agenda body in DataView, selected by the existing date-input Calendar; it introduces no second calendar canvas. Approval and scheduling bind to a saved revision, while the live edition remains immutable until the next release. Its Revisions body now compares two saved snapshots; it does not introduce a second editor or a separate comparison page class.
The Observability app (/labs/apps/observability) separates measured facts from response work: Overview aggregates hourly observations, Services compares current health and ownership, Events traces alerts and deployments, Incidents advances a response, and SLOs accounts for request-based error budgets. Service expressions share services/[key]; incident expressions share incidents/[key]. It composes DataView, DetailPeek, DetailBody, FactCard, FormShell, Timeline and DashboardBoard, with StatTile, ChartContainer, RankBar and Meter. Shared environment/team scope applies everywhere; the observation window filters telemetry but never hides an active incident. Acknowledgement and resolution do not rewrite measurements. Topology reuses ServiceMapCard with that same service scope and latest health; its node opens the shared service peek and detail. Edges denote dependencies, not measured traffic or proven root cause.
Commerce (/labs/apps/commerce) separates orders, payment, fulfillment and returns while sharing CRM customers and the Inventory movement ledger. Billing (/labs/apps/billing) separates subscriptions, usage, issued invoices and collection attempts. Both compose the existing Dashboard/DataView/Detail/Form kits; Billing's customer portal is a view over the same account detail. Their route manifests name every expression; SaaS apps records each model's boundaries and the composed parts.
Three rulings follow:
[key] page; an expression that grows its own detail has forked the object.08
09
This document adds no page classes, and it is not where new ones are added. A skeleton in the index with no class is a shape that has appeared at most once; when it appears a second time, THAT is the moment to legislate — write docs/<class>.md in the house shape (the failure, the thesis, the anatomy, the anti-patterns), extract what the two instances agree on into a kit, add the AGENTS.md row and the codex entry in the same commit, and move the index row from "not legislated" to its class. The new-page skill walks the same procedure, and its first step is this document.