Appearance
The platform model — industry × archetype × primitives × grants
The four-layer model that ADR-0020 to ADR-0025 design, in the compact form the operating manual carried. The ADRs are the decision records (most are 🟡 Proposed, read as approved-pending and being built to); this page is the summary a reader needs before touching industries, archetypes, primitives, grants, the workspace tree or campaigns. The single index of what is decided, built and open remains Current architecture state — read its date first.
Read before changing anything in this model
- This page · 2. ADR-0020 (baseline) ·
- ADR-0021 (three axes) · 4. ADR-0022/0023/0024 (tree, sharing, RBAC) · 5. ADR-0025 · 6. Capability classification. Never branch on archetype, industry or plan in app code — ask for a feature (ADR-0021 D4; not lint-gated, QRS-879). A new primitive needs a written justification that it serves ≥ 3 industries (QRS-391). Your plan must answer: which of the three axes does the change touch, and is the default derived or a sparse grant?
The four layers
Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)
This is the verbatim text of CLAUDE.md § "The platform model: industry × archetype × primitives × grants" as of commit 00c1eca, relocated here under the context-architecture programme. Sentences of the form "this said X until [date]" are corrections recorded at the time they were made; the live rule is the corrected one. Retired vocabulary inside those corrections names what was retired and is not a live claim.
The platform model: industry × archetype × primitives × grants [ADR-0020..0025, designed 2026-08-07/08]
⚠ This section was rewritten 2026-08-08 and SUPERSEDES the previous "Verticals, archetypes & capabilities" text. The old model (5 archetypes · integer
business_domain_id·capabilities/get_my_capabilities) is retired by ADR-0020/0021 and survives only as the S1 code still inapps/mobile, which is scheduled for replacement. Nothing below is built yet — it is the approved-pending design the new Dev baseline implements. Authoritative detail:architecture/current-state.md.
Four layers. The cost of the platform is O(primitives), not O(industries).
INDUSTRY (open set — a row, free to add)
↓ declares
ARCHETYPE (what you sell) × PRIMITIVES (how you run)
↓ resolved through
GRANTS (8 scopes × 3 axes, SPARSE deviations only)- Industry — one
industriesrow, stable TEXT key ('dairy','salon'), never an integer. Integer ids promoted across environments caused QRS-249; a text key makes that hazard class unrepresentable. - Archetype — THREE, not five: Goods (a stocked item) · Time (a slot) · Expertise (an enquiry).
ecommerce_cartandcatalog_informationalwere compositions, not archetypes (QRS-392). - Primitives — the closed set that carries all the cost. Eleven: Catalogue · Party · Schedule · Recurrence · Fulfilment · Ledger · Balance · Location · Resource · Asset · Campaign. A new primitive requires a written justification that it serves ≥3 industries — this is the governing gate of the whole architecture (QRS-391).
- Grants — one
feature_grantstable, 8 scopes × 3 axes (applicability = does this apply · entitlement = is it unlocked and how much · availability = is it shipped).effective = all three.
Applicability DEFAULTS are DERIVED from primitive composition; grants hold only sparse deviations (ADR-0021). Dairy's composition includes Recurrence ⇒ Recurrence-backed features apply. Boutique's omits it ⇒ they don't. Zero admin action. Never build a dense industry × feature matrix — ~30 × ~40 = 1,200 cells nobody maintains.
Three distinctions that must never blur:
- Applicability ≠ entitlement. Does the workflow apply at all (structural) vs is it unlocked and capped (commercial). A free-tier cafe has orders and is capped.
- Feature grant ≠ RBAC permission. Is this workflow available vs can this role do this action on it (ADR-0024). "Agents may view stock but not edit prices" is RBAC.
- Archetype ≠ industry. Archetype = what you sell; industry = who you are. Dairy and boutique are both Goods and differ only in composition — which is exactly why archetype-only gating fails.
Never branch on archetype, industry or plan in app code (ADR-0021 D4). Ask for a feature. ⚠ It is NOT lint-gated, and this said "lint-gated" until 2026-08-28 — archetype has zero occurrences in tooling/eslint-config/. The only thing preventing if (industry === "car_sales") today is discipline (QRS-879). A screen containing if (archetype === 'dairy') is the conditional sprawl the whole model exists to prevent, and it is the one item here that cannot be retrofitted.
Never hardcode an industry list in app code. onboarding/constants/domains.ts did exactly that and its invented ids disagreed with the database — 10 of 12 industry choices would have written the wrong industry (QRS-249).
Enterprise tree and campaigns
Provenance — moved from CLAUDE.md on 2026-09-23 (QRS-1288)
This is the verbatim text of CLAUDE.md § "Enterprise: organization → workspace TREE → members / Campaigns" as of commit 00c1eca, relocated here under the context-architecture programme. Sentences of the form "this said X until [date]" are corrections recorded at the time they were made; the live rule is the corrected one. Retired vocabulary inside those corrections names what was retired and is not a live claim.
Enterprise: organization → workspace TREE → members [ADR-0022/0023/0024]
- The principal is a USER; the WORKSPACE is the business tenant. Membership count distinguishes the three user categories: consumer 0, solo owner 1 (member-owned), enterprise employee 1 (org-owned).
workspacesform a TREE within an organization —parent_id+ a materializedpath(depth cap 6). Not a fixed Org→Location→User hierarchy: depth genuinely varies (retail 2 levels, dealership 3+), and dealerships differ in organising principle (location-P&L vs division-P&L).- A LOCATION is not a business unit. If a place needs its own card and its own P&L it is a WORKSPACE; if it is only an address it is a LOCATION. A showroom is a workspace; a dairy's second collection point is a location.
- SEATS LICENSE USERS, not workspaces (QRS-397). 8 showrooms + 30 agents = 39 cards but 30 seats. Workspace count is capped separately by an entitlement.
- Sharing flows DOWN, oversight flows UP — two mechanisms, never symmetric. Sharing = ADR-0022 flags, default off, a child may use an ancestor's resources. Oversight = RBAC subtree scope, an ancestor may read (never write) descendants' data. ⚠ Oversight applies ONLY to
org_owneddescendants — an employee's personal business and consumer history must never be visible to their employer. - Place a resource at the LOWEST node all its legitimate consumers descend from. Customers at the showroom (so Sales↔Service joins by construction) · vehicle stock at Sales · brand assets at the root.
ownership_model (member_owned | org_owned)does three jobs: seat-revocation behaviour, who keeps the leads, and the oversight privacy boundary.
Campaigns [ADR-0025 — the Enterprise differentiator]
Centrally published, targeted, scheduled, measured offers rendered across many cards. Five of six pieces already exist (sharing · tree targeting · manifest block · analytics · time-bounding). Two rules:
- A first-party
offeris NOT the third-partypromo_slot. The latter fails closed forever pendingcompliance_profile; conflating them either kills campaigns or opens a compliance exposure (ADR-0004). - ⚠ A campaign is the first RENDER-TIME temporal gate, and every prior decision kept those away from rendering because they break the edge cache. Answer: scheduled purge at campaign boundaries via the outbox — never a short TTL (contradicts QRS-381), never client-side (violates ADR-0019 Tier-0 and loses the OG image).