The org-type default change and each organization's enactment lived in the HTTP handlers. They are now entitlements.ChangeOrgTypeDefault and entitlements.EnactDefault, which the reorder handler also calls for each organization. ChangeOrgTypeDefault runs the same steps in the same order: it saves the default (autocommit, outside any lock beyond the row update), composes the actor with the reason "org-type default change (<type>)", lists the type's organizations, and enacts each one. EnactDefault is the old enactOrgDisposition: it finds the default pool, opens BeginMaterializing, locks the pool, reads and classifies its positions, and then initiates the default, counts the pool, or applies the disposition (grandfather through ConferGrantTx per incumbent, migrate through core.end_conferral, ReapplyDefaultsIfVacant and the materialization), each organization in its own transaction and a failure leaving the others alone. The statements, the locks, the rendezvous kind and the legacy grant's description and transition reason are unchanged. Failures come back as EnactFailure values naming the step (eight new steps, from saving the default to the grandfather conferral's outcome); internal/entitlements now imports internal/organization, which imports no console package. CommitOrgTypeDefaultChange keeps the reads, the reserved-type check, the form parse, the candidate's resolution and the gate. renderDefaultChangeFailure gives the two sentences for a failed save and a failed listing, enactFailureReason gives each organization's reason with the driver's text unchanged, orgTypeChangeResult converts the counts for the summary, and logEnactFailures writes the line each failing step wrote before: a warning for a missing pool, errors for the initiation, the grandfather and the migrate's end and restore. The reorder handler passes the new rank 0 as the candidate and logs each type's failures after that type's organizations, as the lines came before. No answer of the default change moves after a rollback: each organization's transaction ended inside the enactment before the card rendered, and still does. Docs: docs/database-locks.md renames enactOrgDisposition to EnactDefault in the pool-row line and the reorder entry, names the default change by entitlements.ChangeOrgTypeDefault and EnactDefault, and gains a correction that rides along: the migrate branch's list now includes ReapplyDefaultsIfVacant, which the migration runs before it materializes. The two model docs point at internal/entitlements/default_change.go, and the call-sites list in the package comment of internal/entitlements now names that file for the default change and the reorder's enactment, in place of the handler. The gocognit exception for enactOrgDisposition (pinned at 38) is deleted from .golangci.yml, with the step heading that only it carried. Measured: CommitOrgTypeDefaultChange 18 at the parent and 16 at this commit; enactOutgoingDefault, the largest new helper, 9; enactDefault 5, grandfatherIncumbents 5, initiateDefault 4, ChangeOrgTypeDefault 3, EnactDefault 1. No output changes. Names and results of ./internal/server, ./internal/entitlements and ./internal/testkit/... are equal at the parent and at this commit after a database reset on each side: 1779 results on each side, 1778 passing and the one existing skip. No test, golden or testdata file is edited and no test ran with -update, so every golden is byte-equal, the two pool-queue tests among them. The seeded operator walkthroughs, which drive the default change through TestOrgTypesWalkthrough, give equal names and results on both sides, and the screens of operator-plan-ladders, plan-ladder-detail (and its open and invalid states) and org-types, desktop and mobile, are byte-identical at the parent and at this commit except the masked operator-integration-stripe shots. Three stored texts leave the handler file: ": grandfathered as legacy", "Grandfathered on org-type default change: " and "org-type default change (%s)" are each found in internal/entitlements; every other literal of the plan-ladders and org-types handler files is still found in a non-test file of internal/server. 21 breaks (38 records), each caught by the old code and by the new code where its text exists on both sides: a grandfathered and an initiated pool are counted, the legacy grant's description and transition reason, a failed restoration's reason, a failed organization turns the toast into an error, an organization with no default pool fails and is logged as a warning, the grandfather conferral must supersede, a failed begin fails the organization, a failed initiation's reason and its log line with the pool, a failed save's sentence, a failed listing after the save, the reason names the type, the new default is saved, a migration to None materializes the pool, each organization's enactment waits on its pool, an organization in contention says a change is in progress, organizations are enacted against the new rank 0, and grandfather reissues the incumbents. None was dropped. Not probed: faults inside each organization's transaction (the position read, the endings, the materialization and the commit; no querier makes them fail), so their reasons and log lines are read but not seen; the undecided refusal, which needs an outgoing-default position that appears between the gate and the enactment; the enactment order among organizations with the same name; and the browser end-to-end plan-management tests, which were not run here.
title, audience, summary
| title | audience | summary | |
|---|---|---|---|
| Domain Model Cards |
|
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).