⛔ Ungraded and unrouted — domain:*, priority and type are triage's. Filed unassigned by the domain:engine execution seat as the implementation carrier for a ruling that already exists.
⚠️ Filed because the ruling's stated carrier is about to disappear. #15706 was ruled and closed at 2026-09-05T05:14:13Z with 「Closing this card as completed by ruling; the work lives on #15225」 — but PR #15687 carries Closes #15225 and is in the merge queue now. When it lands, #15225 closes and the ruled-but-unimplemented seam would have no carrier. This is the same shape as #15710 (filed so Closes #14025 could not close the only record of a reserved question), and it is caught here for the same reason.
The ruling — ⛔ already made, ⛔ not reopened here
From 5549576962 on #15706 (PM seat under the maintainer's direct-dispatch channel, 「这个你也接手」; ⚠️ reversible on one word from the maintainer):
Ruled: (i) — plugin-security records its Layer 0 verdict on the operation; the publish site reads it. ⛔ Not (ii)
Why (i), in the ruling's own terms: plugin-security already computes the verdict — it imports and calls computeTenantLayer0Filter (security-plugin.ts:68), used by both the engine middleware and the public getReadFilter. So (i) records a value that already exists at the moment the wall is applied; (ii) asks the engine to ask a second question that re-derives a slice of that same value. ADR-0131 D8 — 「一道谓词,算一次」 — exists to abolish exactly the mirror this card removes, and ADR-0131 C8 would later delete the provider (ii) would add.
⭐ The binding note for whoever implements it, quoted because it is the difference between fixing this and appearing to:
the verdict recorded must be what the wall decided, not a re-statement of its inputs. If the recorded value can be derived by the reader from anything else on the context, the mirror has not been deleted — it has been moved.
What it closes, and the window it is closing
The bulk producer (publishBulkDataEvent, engine.ts) stamps BulkDataEventSchema.organizationId only when the Layer 0 wall named exactly one organization — and to decide that it re-derives the wall's input. getObjectSecurityMeta folds three clauses into tenancyDisabled; the third, orgScopingEnabled && platformGlobalObjects.has(object) (#12699), is declared by the deployment and is invisible to the engine at any price.
PR #15687 closed the schema half (carriesTenantScopeColumn composes clauses ① ② plus the column). ⚠️ It did not close the deployment half, and it is landing now — so from its merge until this seam lands, a deployment-exempted object under an armed wall can receive a wrong organizationId on a bulk event: 「every affected record belongs to exactly this organization」 asserted falsely, which is the #13566 leak shape reappearing through the PR that closed its bulk half.
⚠️ Two things that bound it, stated so nobody reads this as an active bleed: the population needs a deployment to have configured the #12699 carve-out and an armed wall and a non-admin bulk write — and whether any deployment has that configured is not measured here. The larger, schema-declared population is already closed.
Execution terms, from the ruling — ⛔ not this seat's to vary
What (i) also closes that (ii) would have left open
The ruling notes (i) collapses both 「conservative absent」 populations the engine answers by under-delivering today — a carried PLATFORM_ADMIN rung on a public tenant object, and a non-system context carrying no rung whose exemption is decided by a permission-set probe the engine cannot see. (ii) would have narrowed the mislabel population without closing it, and left both of these to conservatism.
Re-check
git show origin/main:packages/plugins/plugin-security/src/security-plugin.ts | grep -n 'computeTenantLayer0Filter'
# the mirror, as it stands today — comment lines stripped BEFORE numbering:
git show origin/main:packages/objectql/src/engine.ts \
| grep -v '^\s*\*' | grep -cE '(deploymentOrgScopingEntitlement|platformGlobalObjects)[^*]*\.(has|get)\(' # 0
git show origin/main:packages/plugins/plugin-security/src/security-plugin.ts \
| grep -v '^\s*\*' | grep -cE '(deploymentOrgScopingEntitlement|platformGlobalObjects)[^*]*\.(has|get)\(' # 1 — firing control
⚠️ Note on that instrument: a bare count of platformGlobalObjects in engine.ts returns 2 and is no longer a control — those two are JSDoc lines that state the gap. The filter must run before grep -n, or the line number defeats it. Both halves of that lesson were learned on #15706 itself.
Dedup: ⚠️ /search/issues answers 403 in this container and an empty search is not a reading — repo-scoped REST list of open cards plus a local grep; the ruled seam has no open implementation carrier (#15706 is closed as ruled). Related: #15706 (the ruling) · #15225 / PR #15687 (the p0 whose merge opens the window) · #13566 (the parent leak) · #12699 (the deployment carve-out) · #14970 (the single-record half, landed) · ADR-0131 D8 / C8 · contract reviews 5549091499 (FAIL, which found it) and 5550145387 (PASS).
⛔ Ungraded and unrouted —
domain:*, priority and type are triage's. Filed unassigned by thedomain:engineexecution seat as the implementation carrier for a ruling that already exists.Closes #15225and is in the merge queue now. When it lands, #15225 closes and the ruled-but-unimplemented seam would have no carrier. This is the same shape as #15710 (filed soCloses #14025could not close the only record of a reserved question), and it is caught here for the same reason.The ruling — ⛔ already made, ⛔ not reopened here
From⚠️ reversible on one word from the maintainer):
5549576962on #15706 (PM seat under the maintainer's direct-dispatch channel, 「这个你也接手」;Why (i), in the ruling's own terms:
plugin-securityalready computes the verdict — it imports and callscomputeTenantLayer0Filter(security-plugin.ts:68), used by both the engine middleware and the publicgetReadFilter. So (i) records a value that already exists at the moment the wall is applied; (ii) asks the engine to ask a second question that re-derives a slice of that same value. ADR-0131 D8 — 「一道谓词,算一次」 — exists to abolish exactly the mirror this card removes, and ADR-0131 C8 would later delete the provider (ii) would add.⭐ The binding note for whoever implements it, quoted because it is the difference between fixing this and appearing to:
What it closes, and the window it is closing
The bulk producer (
publishBulkDataEvent,engine.ts) stampsBulkDataEventSchema.organizationIdonly when the Layer 0 wall named exactly one organization — and to decide that it re-derives the wall's input.getObjectSecurityMetafolds three clauses intotenancyDisabled; the third,orgScopingEnabled && platformGlobalObjects.has(object)(#12699), is declared by the deployment and is invisible to the engine at any price.PR #15687 closed the schema half (⚠️ It did not close the deployment half, and it is landing now — so from its merge until this seam lands, a deployment-exempted object under an armed wall can receive a wrong
carriesTenantScopeColumncomposes clauses ① ② plus the column).organizationIdon a bulk event: 「every affected record belongs to exactly this organization」 asserted falsely, which is the #13566 leak shape reappearing through the PR that closed its bulk half.Execution terms, from the ruling — ⛔ not this seat's to vary
plugin-sharingmatchingERR_SYSTEM_WRITE_ORGANIZATION_REQUIREDfits neither existing remedy cleanly #14937 precedent): the producer isdomain:engine,plugin-securityisdomain:services. Thedomain:servicesseat adopts on the card; ⛔ no second PR.minoron the package declaring the operation context: adding a member there is an additive widening of a published surface.CONTRACT_REVIEW_TIER(5550145387).What (i) also closes that (ii) would have left open
The ruling notes (i) collapses both 「conservative absent」 populations the engine answers by under-delivering today — a carried
PLATFORM_ADMINrung on a public tenant object, and a non-system context carrying no rung whose exemption is decided by a permission-set probe the engine cannot see. (ii) would have narrowed the mislabel population without closing it, and left both of these to conservatism.Re-check
platformGlobalObjectsinengine.tsreturns 2 and is no longer a control — those two are JSDoc lines that state the gap. The filter must run beforegrep -n, or the line number defeats it. Both halves of that lesson were learned on #15706 itself.Dedup:⚠️
/search/issuesanswers 403 in this container and an empty search is not a reading — repo-scoped REST list of open cards plus a local grep; the ruled seam has no open implementation carrier (#15706 is closed as ruled). Related: #15706 (the ruling) · #15225 / PR #15687 (the p0 whose merge opens the window) · #13566 (the parent leak) · #12699 (the deployment carve-out) · #14970 (the single-record half, landed) · ADR-0131 D8 / C8 · contract reviews5549091499(FAIL, which found it) and5550145387(PASS).