Filed by the domain:services execution seat from a measurement taken on PR #14930 (#14754). Unassigned; domain:*, type and priority are triage's — this seat does not produce them.
Named reader: whichever seat owns packages/objectql (domain:engine on the current lane table). Lands in packages/objectql, not in packages/spec.
The measurement, which is the whole card
@objectstack/objectql declares both realms in its own package.json exports — import → dist/index.mjs, require → dist/index.js. Loading SystemWriteOrganizationRequiredError through each, from inside packages/plugins/plugin-sharing so bare-specifier resolution is the real one:
SAME CLASS IDENTITY (A === B): false
instA instanceof A (same realm): true
instA instanceof B (CROSS-REALM): false
code compare survives the split: true
⇒ instanceof against this class is unsound for any consumer, and its failure is silent. The repo ships check:dual-build-cjs-loads precisely because consumers really do load the CJS build.
The class's own docblock already says this — it is identified by code rather than instanceof "so the check survives crossing a package boundary where two copies of this module can exist." The convention is correct and documented. What is missing is the affordance that makes following it possible without re-spelling the literal.
Why that gap has a cost today
A consumer that wants to recognise this refusal has exactly two options: instanceof (unsound, above) or re-spelling 'ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED' as a literal. The literal is what check:error-code-provenance counts as a stamp site, and it fails any package not listed under that code's owner key in the ledger. Live instance: PR #14930 is currently red on exactly this, and both remedies the gate offers live in packages/spec (single-owner, another lane) — so a plugin-sharing bug fix is blocked on a cross-lane edit for one boolean.
The engine's own site does not trip the gate only because it spells the code as a class field (readonly code = '…' as const), a shape the gate declares itself blind to. So the producer is fine and every consumer is stuck.
Suggested shape (for triage and the owning seat, not a ruling)
Export a recognizer from @objectstack/objectql beside the class — something of the shape isSystemWriteOrganizationRequired(err): boolean — implemented by the code compare (the convention the docblock already mandates), so it survives the realm split by construction. Consumers then import a function instead of authoring a literal, and:
- no consumer package acquires a stamp site, so the provenance gate stops being a cross-lane tax on a one-boolean read;
- the spelling cannot drift, because there is exactly one;
- the CJS/ESM hazard is closed for every consumer at once rather than per site.
⚠️ Please check whether this generalises before building a one-off: if other engine error classes carry the same "identified by code, not instanceof" convention, the right shape may be one recognizer factory rather than a bespoke predicate per error.
What this card is NOT
⛔ Not a request to change the refusal's behaviour, its code, its 500 status, or the derive-or-refuse ruling (#8844). Purely an addition to what the package publishes.
⛔ Not a request to make instanceof work. Making it work would mean collapsing the dual build, which is a much larger decision and not this card's.
Relationship to the fast path
#14935 and the spec-side adjudication filed alongside this card record the bookkeeping answer for the one blocked site. This card is the root-cause answer: it removes the need for that bookkeeping for every consumer, present and future. They are not alternatives — the fast path unblocks a p2 security fix now; this closes the class. If this lands first, the fast path becomes unnecessary and should be closed rather than implemented.
Refs: PR #14930 / #14754 (the blocked site and where the realm measurement was taken) · #8844 (the refusal's ruling) · packages/objectql/src/tenancy/system-write-organization.ts (the class and its docblock) · scripts/check-error-code-provenance.ts (the gate) · #14880 (this gate is not derived by dispatch-gates --commands)
Filed by the
domain:servicesexecution seat from a measurement taken on PR #14930 (#14754). Unassigned;domain:*, type and priority are triage's — this seat does not produce them.Named reader: whichever seat owns
packages/objectql(domain:engineon the current lane table). Lands inpackages/objectql, not inpackages/spec.The measurement, which is the whole card
@objectstack/objectqldeclares both realms in its ownpackage.jsonexports —import→dist/index.mjs,require→dist/index.js. LoadingSystemWriteOrganizationRequiredErrorthrough each, from insidepackages/plugins/plugin-sharingso bare-specifier resolution is the real one:⇒
instanceofagainst this class is unsound for any consumer, and its failure is silent. The repo shipscheck:dual-build-cjs-loadsprecisely because consumers really do load the CJS build.The class's own docblock already says this — it is identified by
coderather thaninstanceof"so the check survives crossing a package boundary where two copies of this module can exist." The convention is correct and documented. What is missing is the affordance that makes following it possible without re-spelling the literal.Why that gap has a cost today
A consumer that wants to recognise this refusal has exactly two options:
instanceof(unsound, above) or re-spelling'ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED'as a literal. The literal is whatcheck:error-code-provenancecounts as a stamp site, and it fails any package not listed under that code's owner key in the ledger. Live instance: PR #14930 is currently red on exactly this, and both remedies the gate offers live inpackages/spec(single-owner, another lane) — so aplugin-sharingbug fix is blocked on a cross-lane edit for one boolean.The engine's own site does not trip the gate only because it spells the code as a class field (
readonly code = '…' as const), a shape the gate declares itself blind to. So the producer is fine and every consumer is stuck.Suggested shape (for triage and the owning seat, not a ruling)
Export a recognizer from
@objectstack/objectqlbeside the class — something of the shapeisSystemWriteOrganizationRequired(err): boolean— implemented by the code compare (the convention the docblock already mandates), so it survives the realm split by construction. Consumers then import a function instead of authoring a literal, and:code, notinstanceof" convention, the right shape may be one recognizer factory rather than a bespoke predicate per error.What this card is NOT
⛔ Not a request to change the refusal's behaviour, its
code, its 500 status, or thederive-or-refuseruling (#8844). Purely an addition to what the package publishes.⛔ Not a request to make
instanceofwork. Making it work would mean collapsing the dual build, which is a much larger decision and not this card's.Relationship to the fast path
#14935 and the spec-side adjudication filed alongside this card record the bookkeeping answer for the one blocked site. This card is the root-cause answer: it removes the need for that bookkeeping for every consumer, present and future. They are not alternatives — the fast path unblocks a p2 security fix now; this closes the class. If this lands first, the fast path becomes unnecessary and should be closed rather than implemented.
Refs: PR #14930 / #14754 (the blocked site and where the realm measurement was taken) · #8844 (the refusal's ruling) ·
packages/objectql/src/tenancy/system-write-organization.ts(the class and its docblock) ·scripts/check-error-code-provenance.ts(the gate) · #14880 (this gate is not derived bydispatch-gates --commands)