Files
member-console/docs/models
cgalo5758 e68493ea16 Read both grants pages from one listing query
ListGrantsWithDelivery now takes optional organization, search, product,
state and paging arguments and serves the grants page and the
organization page's ledger. The grouped SELECT's columns and grouping
moved unchanged into a CTE named listed, whose WHERE now holds the
organization filter and the search filter (with the organizations join
the search needs); the outer statement filters on the state computed
once, counts with count(*) OVER() after that filter, orders by
created_at DESC and pages (a NULL limit or offset reads every row).
ListGrantsWithDeliveryPage, toDeliveryRows and loadGrantsListData's
unused success and error-message arguments are deleted. The generated
row is field for field the old page row. Product names are still
matched in Go, so % and _ in a search keep matching themselves there.

The two pages keep the pool difference: with an organization the waits
are evaluated on its active default pool, without one on every pool.

How the commit was shown to change no output:
- The row golden, run first on PostgreSQL against the merged query,
  passed with every file under testdata/grants_listing/ and
  testdata/org_enrollment/ unchanged. LIMIT NULL OFFSET NULL returns all
  rows, and EXPLAIN ANALYZE of the organization-only call and of the
  search, state and page call shows no CTE scan on listed (it is inlined).
- internal/server (1398 results) and internal/entitlements (180) give
  the same test and subtest names and results at the parent and at this
  commit; no test was added, renamed or deleted.
- 13 breaks, each caught by the old and the new test, plus 3 breaks
  caught on the old side only: the adapter's copies of the waits, the
  default-duplicate and the restored-after columns, which are deleted
  with the adapter. No break was dropped because the old test missed it.
- Test calls were re-pointed to the merged query with their assertions
  unchanged (every row of the assertion map is same). The comments of the
  query, the grants template, docs/models/entitlements.md and
  docs/operator-ux-conventions.md now name the one query.

Unprobed: the order of grants that share created_at (a tier removal
writes one default grant per vacated pool in one transaction). The
merged query's plan may order such a tie differently from either old
query, no fixture holds a tie, and the order is accepted until the
tie-break by grant id, as for positions. The zero UUID pool of an
organization with no active default pool: no fixture organization lacks
one. total_count on the organization call, which no caller reads. The
adapter's other 22 copied columns on the old side: the goldens read each,
three are probed, and the adapter is gone so no copy remains to break.
2026-10-10 03:36:06 -05:00
..

title, audience, summary
title audience summary
Domain Model Cards
developer
One card per domain model: purpose, invariants, dimensions, drift traps, and open questions, each claim cited to the migration, file, or decision that enforces it.

Domain Model Cards

Each card in this directory describes one domain model as it is actually built, so a maintainer can modify that model without re-deriving its rules from migrations, specs, and decision documents. The cards are the descriptive home for the as-built models; the normative record — the designed schema, the decision log, the numbered design documents, and the glossary — lives in the synced design/ directory (Decision 141, design/documents/doc-43-schema-topology-and-documentation-division.md). Migrations are the ground truth for as-built claims.

The catalog was motivated by a 2026-07 inventory of the console's domain models, which traced the majority of a 56-finding UX audit to model-attributable causes: rules that lived only in prose, invariants enforced nowhere, and dimensions that were easy to conflate because no single page named them side by side.

The cards

  • Product / catalog — how capabilities are packaged, priced, and sold; the dissolved kind taxonomy and the shape views that replaced it.
  • Payments and Billing — billing accounts, subscriptions, invoices and payments projected from Stripe, scheduled changes as intent, and the reconciler.
  • Plan Ladders and Transitions — ranked tiers, who occupies which rung, the append-only audit, and the conferral functions as sole writers.
  • Entitlements — resource keys, sets and rules, grants as decrees, provisions as delivery, and the materializer.
  • Resource Pools — the per-organization container: workspace assignments, the two resolution directions, and pool-grain usage.
  • Providers and Integrations — the registry and its connections, boot-time manifests, resource-key ownership, desired-state delivery, and the shared substrate.
  • Domains Registry — claims as subtree ownership, placements as the only thing that serves, and the anti-abuse ledger.
  • Identity, Organizations, and Workspaces — users vs persons, the containment ladder, org types as policy, and first-login provisioning.

A new domain model is not considered documented until its card exists and is listed here.

Card anatomy

Every card has four sections in this order:

  • Purpose — what the model owns and why it exists, in plain present tense.
  • Where this lives — a map of the model's pieces: each table, view, function, and code file, with one line saying what it is. Locations live here, not scattered through the body.
  • Invariants — the rules the model guarantees, one per numbered entry so reviews can cite "invariant 3". Each entry carries an enforcement tag matching the actual mechanism: [db] means the database schema rejects violations outright; [db-code] means the database functions are written to uphold the rule, so reviews must protect it when they change; [app] means only application code upholds the rule, so new code paths must uphold it too.
  • Dimensions — the orthogonal axes a maintainer must hold simultaneously, each defined where it is named.

Cards describe the model as it is today, for a reader who knows nothing yet. That means: no history, no decision numbers, no change narrative in the body — a card introduces every table, column, view, and concept before or where it first uses it. Drift between design and code, open questions, and other bookkeeping live on the forge tracker, not in the cards.

Style contract

Card prose follows these rules, and edits are reviewed against them:

  • Write for someone with zero context: never assume the reader knows a term, a table, or an internal codename you haven't introduced.
  • Name the agent: state what acted and what it acted on.
  • Give each verb its own object; do not let one object serve two verbs.
  • Connect clauses with explicit causal or temporal words (because, so, when, after) — never with a bare comma.
  • Replace every metaphor or compressed phrase with the literal claim it stands for.
  • Give every count or statistic its denominator.

A change that alters an invariant, dimension, or map entry recorded on a card must update that card in the same change (see openspec/specs/model-catalog/spec.md once this change archives).