Status: procedure reference. The rules — schema audit before large features, Organization as canonical identity, use the shared primitives — live in AGENTS.md §11 and stay always-on. This file holds the primitive inventory, helper boundaries and audit indicators. Relocated by BI-0020D511 Phase 1; no rule was dropped.
Before adding any large feature, audit the existing schema for refactoring opportunities. Indicators that refactoring is needed: a domain model being reused as a shared concept; the same logical data appearing in two+ existing models; a new feature needing meta-data with no canonical home. → kernel principle
Organization is the canonical platform identity model. Any feature needing org name, slug, logo, address, or contact info reads from Organization — not from BrandingConfig, env vars, or bespoke fields elsewhere. → kernel principle
The Organization.address JSON has one canonical shape + helpers in apps/web/lib/shared/org-address.ts (OrgAddress, parseOrgAddress / serializeOrgAddress / formatOrgAddressLines, resolveTimezoneFromAddress). Read and write the address through those — do not hand-roll a parallel address field or shape. It is captured at setup via the business-context step (/storefront/settings/business) and is the precise source for state-accurate timezone derivation (BI-AAAA0691).
Shared micro-primitives (BET-6, BI-6A505BFF). Cross-cutting helpers that were hand-inlined at hundreds of sites now have one home each — import them, do not re-copy:
- Server-action result:
apps/web/lib/shared/action-result.ts—ActionResult<T>({ ok: true; data: T } | { ok: false; error: string }) withok(data?)/err(message)constructors. The canonical shape for a server action’s return. - JSON coercion:
apps/web/lib/shared/coerce.ts—isRecord(v)(object guard),asString(v, fallback?),asNumber(v, fallback?)for narrowingPrisma.JsonValue/unknown. A CI ratchet (scripts/check-no-local-isrecord.mjs) freezes the count of legacy localisRecordcopies; new code must import this one. - Route paths:
apps/web/lib/routes.ts—ROUTES.*named constants for the high-frequency section roots passed torevalidatePath/redirect/<Link>, so a rename is a single compiler-checked edit.
Route-segment helpers vs shared domain modules (BI-IMP-BC5AA87E). Page-local helpers under a route segment (e.g. apps/web/app/(shell)/…/_helpers.ts or a colocated *.ts next to page.tsx) may format, adapt, or present canonical domain data for that surface only. They must not invent a second home for reusable agent/delegation policy, tool metadata, grant maps, persisted outcome contracts, or identifiers that other routes need — those live in shared modules under apps/web/lib/ (or packages). If a helper is imported from a second route or encodes policy that would change coworker behavior platform-wide, promote it to a shared canonical module in the same PR.
Metadata governance (BI-IMP-FA900452, BI-IMP-52761525). JSON metadata columns (e.g., unstructured payload fields) are reserved for optional, unstructured, or rapidly-evolving context. They must not be used as primary query or reporting sources. Any property that becomes frequently filtered, queried, or joined must be promoted to a typed schema field (via a database migration). When reading or writing JSON metadata in code, use typed accessor helpers and centralized key constants rather than raw string indexing. A provider publication/thread/conversation id is normalized through the owning domain’s canonical identity or relationship mapping before a report joins on it; an incidental metadata key is never an implicit foreign key. → kernel principle · reporting boundary
Principal convergence (2026-05-09). Per the addendum on docs/superpowers/specs/2026-04-22-enterprise-auth-directory-federation-design.md, any new identity-bearing entity introduced after 2026-05-09 must be modeled as a PrincipalAlias linked to a single Principal, not as a parallel identity table. The convergence target covers User, CustomerContact, Agent, EdgeNode, MobileDevice, and ServiceAccount. Authorization decisions resolve on the Principal; alias kind tells the platform which surface authenticated the request. → kernel principle
Record lifecycle convention
The ONE “not active” convention (BI-C357FA5A, Simplify & Strengthen W20, architecture pass 2026-08-16 §3.2-d). The pass measured “not active” said six ways across the schema — archivedAt, supersededById, mergedIntoId, quarantinedAt/overlapQuarantinedAt/conflictQuarantinedAt, retiredAt, and status="quarantined" — so every reader had to know which convention applies per table. The unified convention is:
lifecycle RecordLifecycle @default(active)— the enum (declared inpackages/db/prisma/schema/resource-scheduling.prisma) closes the state set:active | archived | retired | superseded | merged | quarantined. A record isactiveor it carries exactly one not-active state.lifecycleAt DateTime?— when the record leftactive. NULL while active.lifecycleReason String?(optional) — free-text operator/system context, includinglegacy-*:markers minted by convention migrations.- Successor pointers (supersede/merge chains) are declared self-relations with a leading index in the owning family — never bare
*Idcolumns (the FK-index ratchet enforces the declared+indexed half).
Semantics per state: archived = kept for reference, excluded from operational reads; retired = permanently withdrawn from service; superseded = replaced by a successor record (pointer required); merged = collapsed into a survivor record (pointer required); quarantined = excluded pending integrity review (the record is suspect, not the data subject).
Enforcement. scripts/check-no-new-notactive-conventions.mjs blocks NEW legacy-convention columns schema-wide; the 29 existing carriers live in the shrink-only owned baseline scripts/notactive-conventions-baseline.json (owner platform-architecture). New String status columns carrying quarantine vocabulary are already blocked by scripts/check-no-new-closed-set-strings.mjs.
Pilot family. The W19 unified resource-scheduling models (Resource, ResourceAvailability, ResourceCapacityPool, ResourceCapacityAllocation) are born on the convention — zero-data adoption. Existing carriers migrate per the operator-reviewed plan: docs/superpowers/plans/2026-08-18-w20-lifecycle-convention-migration-plan.md. Business-state machines (e.g. an allocation’s reserved→confirmed→released flow) remain domain enums — the record lifecycle says whether the ROW is live, not where the BUSINESS process stands.