Filed by the #8459 dev agent, measured while implementing that card's ruling. Unassigned, out of #8459's scope — that card ruled on who declared the column; this is a different axis (which opt-out row was used) and needs its own decision.
The disagreement
Two places answer "is this object tenant-scoped", and they read different declarations:
| declaration | resolveInjectedSystemColumns().tenant (spec, drives the index) | plugin-security's tenancyDisabled (drives the wall) |
|---|
tenancy.enabled: false | withholds | disabled — no wall |
systemFields.tenant: false | withholds | disabled — no wall |
managedBy: 'better-auth' | withholds | n/a (no org column exists) |
systemFields: false | withholds | NOT disabled — wall still applies |
security-plugin.ts derives it as exactly two clauses:
consttenancyDisabled=(objasany)?.tenancy?.enabled===false||(objasany)?.systemFields?.tenant===false;
systemFields: false — the hard object-level opt-out — is not among them. And Layer 0's other gate is objectFields.has('organization_id'), so an object using the hard opt-out while declaring its own organization_id gets the wall predicate AND-composed onto essentially every read, with no platform index behind it.
Measured
applySystemFields(obj, { multiTenant: true }).indexes, on origin/main plus the #8459 change:
systemFields: false + author-declared organization_id => null
systemFields.tenant: false + author-declared organization_id => null
plain object + author-declared organization_id => [{"fields":["organization_id"]}] (#8459)
Row 2 is correct and deliberate: security agrees the object is not walled, so there is no predicate for an index to serve. Row 1 is the disagreement — the only row where the wall filters on a column the platform declines to index. Same "hottest predicate unindexed" shape #8459 closed, reached by a different route, and not introduced by #8459 (it withheld the index for these objects before that change too, via the injected-column condition).
The security half is asserted from the source above, not measured end to end; the objectql half is measured.
The decision this needs
Which derivation is authoritative for systemFields: false?
A and B differ in observable behaviour, so this is not a pure refactor and should not be self-adjudicated. Note also that they disagree about which side is the defect, which is why no option here is obviously "the fix".
Dedup
Searched: systemFields false hard opt-out tenancyDisabled organization_id tenant wall, tenancyDisabled cache systemFields false not covered security plugin. Zero open neighbours. Nearest related, none a twin: #8459 (whose declaration governs the index), #8323 (tenant-scoped UNIQUE indexes going global — driver DDL normalization), #6810 (moved the tenant index into indexes[]).
Related
#8459 · #8375 · #6810 · #7865 (the convergence map option C would extend) · ADR-0105
Generated by Claude Code
Filed by the #8459 dev agent, measured while implementing that card's ruling. Unassigned, out of #8459's scope — that card ruled on who declared the column; this is a different axis (which opt-out row was used) and needs its own decision.
The disagreement
Two places answer "is this object tenant-scoped", and they read different declarations:
resolveInjectedSystemColumns().tenant(spec, drives the index)tenancyDisabled(drives the wall)tenancy.enabled: falsesystemFields.tenant: falsemanagedBy: 'better-auth'systemFields: falsesecurity-plugin.tsderives it as exactly two clauses:systemFields: false— the hard object-level opt-out — is not among them. And Layer 0's other gate isobjectFields.has('organization_id'), so an object using the hard opt-out while declaring its ownorganization_idgets the wall predicate AND-composed onto essentially every read, with no platform index behind it.Measured
applySystemFields(obj, { multiTenant: true }).indexes, onorigin/mainplus the #8459 change:Row 2 is correct and deliberate: security agrees the object is not walled, so there is no predicate for an index to serve. Row 1 is the disagreement — the only row where the wall filters on a column the platform declines to index. Same "hottest predicate unindexed" shape #8459 closed, reached by a different route, and not introduced by #8459 (it withheld the index for these objects before that change too, via the injected-column condition).
The security half is asserted from the source above, not measured end to end; the objectql half is measured.
The decision this needs
Which derivation is authoritative for
systemFields: false?organization_idwithholds the platform's tenant index — the wall's hottest predicate runs unindexed on a multi-tenant deployment #8459's ruling logic, one row over). Fix inresolveInjectedSystemColumns' consumer or in the index predicate.systemFields: falsemeans the object has no platform tenant semantics at all, so plugin-security should treat it as tenancy-disabled and stop composing the predicate.resolveInjectedSystemColumnsis already the spec's single derivation andtenancyDisabledCacheis a hand-rolled second reading of the same declarations — the [Decision]applySystemFieldsinjects platform anchors intoexternalobjects the platform provisions no storage for — three consumers have now independently re-derived "that column is not really there" #7865 convergence map lists exactly this species of duplicate.A and B differ in observable behaviour, so this is not a pure refactor and should not be self-adjudicated. Note also that they disagree about which side is the defect, which is why no option here is obviously "the fix".
Dedup
Searched:
systemFields false hard opt-out tenancyDisabled organization_id tenant wall,tenancyDisabled cache systemFields false not covered security plugin. Zero open neighbours. Nearest related, none a twin: #8459 (whose declaration governs the index), #8323 (tenant-scoped UNIQUE indexes going global — driver DDL normalization), #6810 (moved the tenant index intoindexes[]).Related
#8459 · #8375 · #6810 · #7865 (the convergence map option C would extend) · ADR-0105
Generated by Claude Code