Appearance
ADR-0022 · Organization resource sharing
Status: 🟡 Proposed — authored 2026-08-08, awaiting owner approval · Amends: ADR-0020 (tenancy), ADR-0001 · Depends on: ADR-0006 (role scope), ADR-0021 (feature grants) · Related: industry scope, user ecosystem
The one-line thesis
The automotive-dealership case exposes a gap ADR-0020 cannot express: a dealership has ONE stock of cars and twenty agents selling from it. catalog_items.workspace_id forces either twenty divergent copies or zero visibility. The fix is resolution over the organization, not duplication into it — members own their data, an organization may own shared resources that members resolve as a union, and nothing is shared unless a policy flag says so.
Context
Car dealerships are the first planned Enterprise customer, and the requirement has two halves that must run on one foundation:
- Independent car sales agent — a solo business owner with their own card, own vehicle listings, own leads and customers.
- Dealership — a seat-based Enterprise subscription, an org admin managing agents, centralized billing, users, permissions and features, and potentially shared inventory, service, and dealership-level information.
Assessed against ADR-0020 as written, four of the five requirements already hold. One does not, and it is the one that matters commercially.
What already works, verified against the model
| Requirement | Mechanism | Verdict |
|---|---|---|
| Independent agent as a solo business | workspaces (kind='solo', ownership_model='member_owned'), own cards row, own catalogue and Party rows | ✅ No change |
| Dealership as an enterprise tenant | organizations + org workspace + workspace_members | ✅ No change |
| Seat-based subscription, centralized billing | billing_accounts(subject_kind='organization') → subscriptions → seats → member workspaces | ✅ No change |
| Org admin controls features and permissions | Two mechanisms, deliberately distinct — see D3 | ✅ No change |
| Agent leaves the dealership | ownership_model='org_owned' ⇒ seat revoked ⇒ workspace suspended, card stops resolving, leads stay with the dealership | ✅ No change, and correct |
Three of those are worth calling out as validation rather than luck, because they follow from decisions already taken for other reasons:
- An organization has no archetype; its member workspaces do. So a dealership can run sales workspaces (Expertise) and a service department (Time — service-appointment booking) inside one organization, with one subscription. This falls straight out of "workspace is the business tenant" and would have been impossible had archetype sat on the organization.
- The dealership's own corporate card and each agent's card coexist — separate workspaces, one
cardsrow each, no special case. ownership_modeldecides who keeps the leads when an agent leaves, and it is the same column that lets an MLM distributor keep their card. A dealership does not want its lead list walking out of the door; a distributor's book is their own. One column, both requirements, no branch.
Archetype and industry are also sufficient here. A car agent is Expertise — a ₹8 lakh car is not bought on a card; the card generates the enquiry (fit test F3). Vehicle listings are ordinary Catalogue rows whose attributes carry {make, model, year, km_driven, fuel, variant, colour, registration_state, owners}. This is structurally identical to a real-estate agent, already mapped the same way — which is the archetype model working, not straining.
The gap: shared inventory is unrepresentable
catalog_items.workspace_id means an item belongs to exactly one workspace. For a dealership with 200 cars and 20 agents that leaves two options, both wrong:
| Option | Why it fails |
|---|---|
| Copy the inventory into each agent's workspace | 20 divergent copies. Worse, the exclusivity guarantee breaks: agent A sells the red Swift and agents B–T still show it available. This is the "one 3-foot idol cannot be sold five times" problem, multiplied by seats. |
| Keep inventory on the org's workspace only | RLS scopes reads by workspace membership, so no agent can see it — and no agent's public card can render it. |
This is a foundation-level gap, not a feature gap. It cannot be patched with a feature grant, because feature grants control whether a workflow is available, never whose data a query can reach.
Decision
D1 — Resolution over the organization, as a UNION. Never duplication
An organizations row may own shared resources. A member workspace resolves:
visible resources = own workspace ∪ { organizations it belongs to, where sharing is enabled for that type }One row per car. Twenty agents. Exclusivity holds by construction, because there is only ever one row to mark sold — which is precisely the argument that decides this against duplication.
It is a UNION, not an override, and that distinction is load-bearing: a dealership agent may legitimately have both dealership stock and their own privately-sourced used car on their card. This differs from the appearance resolver (ADR-0019 G4: member → org → archetype → platform, where the nearest wins) — so the two resolvers must not be implemented from a shared "org resolution" helper that assumes override semantics.
D2 — Sharing is per-resource-type, policy-flagged, and DEFAULT OFF
A small closed set of shareable types, each with an explicit organization policy flag:
| Shareable | Default | Notes |
|---|---|---|
| Catalogue (inventory, price list) | off | The dealership case. Also brokerage listings, franchise product sets |
| Location (showrooms, branches) | off | Pairs with QRS-385 |
| Resource (test-drive cars, service bays, staff) | off | Pairs with QRS-386 |
| Branding / template | off | Already a resolver (ADR-0019); this just gives it its flag |
| Party (customers, leads) | off, and the most sensitive | See the warning below |
⚠ Party sharing is the one that can cause real harm, so it is called out rather than listed. A dealership plausibly wants shared customer records; an MLM upline must never see a downline distributor's customers, and an employee's personal side business and consumer history must never be visible to their employer (the QRS-386 finding). Default off, explicitly enabled, and scoped per organization — never per user.
Default-off is the whole safety property. Sharing that must be switched on cannot leak by omission; the reverse can, and would do so silently, in the direction of a privacy breach.
D3 — Feature grants and RBAC permissions are different mechanisms, and the org controls both
The requirement "the dealership decides agents may view stock but not edit prices" is not a feature grant. Stating this explicitly because conflating the two produces exactly the tangled conditional logic ADR-0021 exists to prevent:
| Question | Mechanism | Example |
|---|---|---|
| Is this workflow available to this business? | feature_grants (ADR-0021) | The org's Enterprise plan includes Catalogue |
| Can this role perform this action on it? | roles/permissions (ADR-0006) | Agent has catalog:view, not catalog:edit |
Both are org-controllable — feature_grants at workspace_group/workspace/workspace_member scope, permissions via roles.scope='organization'. Neither substitutes for the other, and a shared resource needs both: visible via D1, editable only per D3.
D4 — Sharing is an organization property, so it needs no new tenancy concept
No new table for "dealership". No dealership_inventory. The mechanism is: organizations gains sharing policy flags, shareable tables gain an owning-organization reference, and RLS gains one union clause. A dealership, a real-estate brokerage, a franchise network and an insurance agency are then the same configuration — which is the ≥3-industry test ADR-0021 requires of any structural addition, and it passes:
- Car dealership — shared vehicle inventory across agents ✅
- Real-estate brokerage — shared property listings across agents ✅ (very common at 5–20 agents)
- Franchise network — shared product catalogue and pricing across franchisees ✅
- Insurance / travel agency — shared product set across sub-agents ✅
- MLM / direct selling — ❌ shares nothing; distributors buy their own stock. The useful negative that proves the flag is necessary rather than a default.
Options considered and rejected
| Option | Why rejected |
|---|---|
| Duplicate inventory per agent | Breaks exclusivity — the decisive failure. Twenty copies diverge within a day. |
Polymorphic owner_kind (workspace|organization) on every shareable table | Complicates every query and every policy, and the second ownership axis spreads (does media get it? analytics_events?). D1 achieves the same visibility with a resolution rule and one owner column. |
catalog_shares(item_id, workspace_id) link table | 200 cars × 20 agents = 4,000 trigger-maintained rows that must be reconciled on every seat change. A resolution rule has no rows to drift. |
| Containment — member data belongs to the organization | Powerful for dealerships and fatal everywhere else: it destroys the MLM case, and it makes an employee's personal business and consumer history visible to their employer. Rejected on privacy, not on modelling. |
| A separate "dealership" module | The isolated-implementation outcome this whole architecture exists to avoid. |
Long-term scalability assessment
The question underneath the dealership case is what relationship an organization has to its members' data, and the answer determines how far the platform scales up the customer-size curve:
| Model | Independent vendor | Group / dealership | Franchise | Large enterprise | MLM |
|---|---|---|---|---|---|
| Isolation (ADR-0020 as written) | ✅ | ❌ no shared stock | ❌ | ❌ | ✅ |
| Containment | ❌ | ✅ | ✅ | ✅ | ❌ privacy breach |
| Resolution + explicit sharing (D1/D2) | ✅ | ✅ | ✅ | ✅ | ✅ |
One mechanism covers the whole curve, from a solo Ganapati stall vendor to a multi-showroom dealership group, because sharing is a policy rather than a shape. Extensibility is additive by construction: a new shareable type is one flag plus one union clause, and a new org tier is workspace_group rows.
It is also the fourth resolver of the same family already in the design — feature grants, appearance, capabilities, and now shared resources. Consistency here is worth more than local optimality: one mental model, one place to look for precedence bugs.
Consequences
Schema (additive, must be in the baseline): organizations gains sharing policy flags · catalog_items, locations, resources, parties gain a nullable owning-organization reference · every affected RLS policy gains one union clause · pgTAP proving the negative (an agent of org A can never read org B's inventory, and a member of an org with sharing off reads nothing shared).
Cost: ~+0.75d in the baseline. If deferred: changing catalog_items ownership after real inventory exists is a data migration across the table the entire public card renders from, plus a rewrite of every catalogue policy — the same class as QRS-385/QRS-387.
Accepted cost, stated plainly. Every shareable table's RLS gains a second clause, so tenant reads get marginally more expensive and the QRS-382 RLS performance discipline becomes mandatory rather than advisable — the union must be index-backed (workspace_members(user_id, workspace_id) plus an org-membership index), or shared-catalogue reads degrade exactly where the customer is largest. That is the trade: one mechanism for the whole customer-size curve, paid for with one extra indexed clause on tenant reads.
Recommendation
Adopt D1–D4 now, in the baseline. The dealership requirement is not a future maybe — it is the first Enterprise customer, and it is the only one of the five requirements that ADR-0020 cannot currently express. Everything else in the automotive case already works, which is the strongest available evidence that the tenancy model is right and needs one addition rather than a rethink.