Found by the cloud-side group-posture showcase (objectstack-ai/cloud#880) — the first end-to-end boot of group posture against a real driver. Part of ADR-0105 tracking #3541.
Symptom
Boot a real stack (@objectstack/verify bootStack + @objectstack/organizations, OS_TENANCY_POSTURE=group), seed one group org + three plant orgs, a group admin holding memberships in all four. Every read that group admin makes returns only the active organization's rows; organization/set-active MOVES their read reach. That is isolated semantics — the ADR-0105 D2 union (organization_id IN accessible_org_ids) never applies.
Root cause
The Layer 0 side is correct end-to-end — instrumenting computeLayeredRlsFilter during the failing reads shows posture: 'group' and the full 4-org accessible_org_ids set arriving on the context. The collapse happens BELOW it:
packages/objectql/src/engine.ts (~line 861): whenever the object is tenancy-enabled and execCtx.tenantId is set, the engine unconditionally propagates it into DriverOptions.tenantId — no posture awareness.
packages/plugins/driver-sql/src/sql-driver.ts (tenantFieldByTable doc, ~line 393): with DriverOptions.tenantId set, the driver natively scopes reads/updates/deletes/aggregates to organization_id = tenantId.
So the driver ANDs an active-org equality under the Layer 0 union, and equality wins. The unit suite (tenant-layer.test.ts) proves the pure filter compiler; the driver's independent native scoping is only exercisable in a full boot with the enterprise runtime — which the open-core dogfood deliberately skips (rls-multitenant.dogfood.test.ts: "enterprise/cloud CI, which ships the package, runs this").
Expected
Under group posture the driver-level scope must not narrow reads below the Layer 0 union: either don't propagate the active-org equality scope to the driver for reads when postureUsesUnionScope(posture) (the Layer 0 filter already carries the wall), or propagate the accessible set so native scoping can compile IN. Write-side stamping (insert default from active org) must keep its current behavior.
Repro
apps/ee-group-showcase in cloud (added by the PR attached to cloud#880): test/group-posture.dogfood.test.ts pins the two collapsed assertions as it.fails — bumping cloud's .objectstack-sha past the fix flips them red, which is the signal to un-pin and assert the real union semantics.
Refs: ADR-0105 D1/D2, #3541, #3559, #3570; cloud#880.
Found by the cloud-side group-posture showcase (objectstack-ai/cloud#880) — the first end-to-end boot of
groupposture against a real driver. Part of ADR-0105 tracking #3541.Symptom
Boot a real stack (
@objectstack/verifybootStack+@objectstack/organizations,OS_TENANCY_POSTURE=group), seed one group org + three plant orgs, a group admin holding memberships in all four. Every read that group admin makes returns only the active organization's rows;organization/set-activeMOVES their read reach. That isisolatedsemantics — the ADR-0105 D2 union (organization_id IN accessible_org_ids) never applies.Root cause
The Layer 0 side is correct end-to-end — instrumenting
computeLayeredRlsFilterduring the failing reads showsposture: 'group'and the full 4-orgaccessible_org_idsset arriving on the context. The collapse happens BELOW it:packages/objectql/src/engine.ts(~line 861): whenever the object is tenancy-enabled andexecCtx.tenantIdis set, the engine unconditionally propagates it intoDriverOptions.tenantId— no posture awareness.packages/plugins/driver-sql/src/sql-driver.ts(tenantFieldByTabledoc, ~line 393): withDriverOptions.tenantIdset, the driver natively scopes reads/updates/deletes/aggregates toorganization_id = tenantId.So the driver ANDs an active-org equality under the Layer 0 union, and equality wins. The unit suite (
tenant-layer.test.ts) proves the pure filter compiler; the driver's independent native scoping is only exercisable in a full boot with the enterprise runtime — which the open-core dogfood deliberately skips (rls-multitenant.dogfood.test.ts: "enterprise/cloud CI, which ships the package, runs this").Expected
Under
groupposture the driver-level scope must not narrow reads below the Layer 0 union: either don't propagate the active-org equality scope to the driver for reads whenpostureUsesUnionScope(posture)(the Layer 0 filter already carries the wall), or propagate the accessible set so native scoping can compileIN. Write-side stamping (insert default from active org) must keep its current behavior.Repro
apps/ee-group-showcasein cloud (added by the PR attached to cloud#880):test/group-posture.dogfood.test.tspins the two collapsed assertions asit.fails— bumping cloud's.objectstack-shapast the fix flips them red, which is the signal to un-pin and assert the real union semantics.Refs: ADR-0105 D1/D2, #3541, #3559, #3570; cloud#880.