Filed by the domain:engine execution seat from a census taken while building #16156 (the recognizer for SystemWriteOrganizationRequiredError). ⛔ Observation, not a runtime defect — no assignee, no pm:queue; triage grades it.
The census
packages/objectql/src, non-test source. 18 classes extend Error; 17 carry a readonly code field. Of those:
| file:line |
code |
driver-connect-errors.ts:99 |
ERR_DRIVER_CONNECT |
driver-connect-errors.ts:174 |
ERR_DATASOURCE_UNAVAILABLE |
readonly-strict-errors.ts:161 |
ERR_READONLY_FIELD_REJECTED |
registry.ts:1292 |
NAMESPACE_CONFLICT |
registry.ts:1404 |
DUPLICATE_ARTIFACT_OBJECT_NAME |
registry.ts:1463 |
OBJECT_OWNERSHIP_CONFLICT |
summary-errors.ts:24 |
ERR_SUMMARY_RECOMPUTE |
transaction-errors.ts:26 |
ERR_TRANSACTION_UNSUPPORTED |
transaction-errors.ts:64 |
ERR_CROSS_DATASOURCE_TRANSACTION_WRITE |
validation/record-validator.ts:136 |
VALIDATION_FAILED |
tenancy/system-write-organization.ts |
ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED — discharged by #16156 |
Why this is worth a card rather than a shrug
The reasoning is the one #14936 established, and it applies unchanged to each row: @objectstack/objectql declares both realms in its own exports (import to dist/index.mjs, require to dist/index.js), so a consumer holding the other realm's copy gets instanceof false — measured, and silent. A consumer's only sound option for these 11 is to re-spell the literal, which is what check:error-code-provenance counts as a stamp site in the consumer's own package, and which can drift from what the engine throws with no compile error.
⭐ The docs already teach the convention while offering nothing to import. content/docs/kernel/contracts/data-engine.mdx:361 says, of ReadonlyFieldRejectedError:
Catch it by code, not instanceof, and read drops for the per-reason breakdown
and ERR_READONLY_FIELD_REJECTED is row 3 above — an inline literal with no exported constant. So the published guidance and the published surface disagree today, for at least one error, in the documentation's own words.
Measured alongside: none of the five existing *_CODE constants appears anywhere in content/docs (0 files each; control @objectstack/objectql fires on 12 files). So no page enumerates these, and no page becomes stale by adding one.
Suggested shape, for triage rather than a ruling
Per-error convergence onto the existing five-precedent shape — export const X_CODE = '…' as const; with the class field reading from it, re-exported from index.ts. ⛔ Deliberately not one recognizer factory: #16156 measured that a factory's signature (makeRecognizer(code)) still requires every call site to supply the code, so it relocates the literal rather than removing it; and the 14 bespoke recognizers already in the repo across 8 packages compare three different things (a brand key, code-or-name-or-message, a cause walk with a depth budget), which no single factory fits.
⚠️ Each row is a published-surface widening and therefore its own Clause-② decision plus a minor changeset — which is why this is a sweep to be scheduled and graded, not a rider on any one PR. Splitting it per file (or per error family) is probably right.
⚠️ Two rows deserve checking before conversion rather than mechanical treatment: EMPTY_CREDENTIAL_REFUSAL_CODE is already 'VALIDATION_ERROR' and record-validator.ts uses 'VALIDATION_FAILED' — two different spellings of a validation refusal in one package, and whether they should converge is a question this card does not answer.
Refs: #14936 · #16156 · content/docs/kernel/contracts/data-engine.mdx · packages/spec/scripts/check-error-code-provenance.ts
Generated by Claude Code
Filed by the
domain:engineexecution seat from a census taken while building #16156 (the recognizer forSystemWriteOrganizationRequiredError). ⛔ Observation, not a runtime defect — no assignee, nopm:queue; triage grades it.The census
packages/objectql/src, non-test source. 18 classes extendError; 17 carry areadonly codefield. Of those:DUPLICATE_RECORD_CODE,HOOK_TARGET_REBIND_ERROR_CODE,HOOK_UNSCOPED_DATA_ACCESS_CODE,MULTI_UPDATE_HOOK_KEY_DIVERGENCE_CODE,EMPTY_CREDENTIAL_REFUSAL_CODE. Four are exported from the package barrel. A sixth joins them when feat(objectql): publish SYSTEM_WRITE_ORGANIZATION_REQUIRED_CODE and isSystemWriteOrganizationRequiredError so consumers stop re-spelling the literal #16156 lands.typeofannotation —nav-contribution-diagnostics.ts:93.driver-connect-errors.ts:99ERR_DRIVER_CONNECTdriver-connect-errors.ts:174ERR_DATASOURCE_UNAVAILABLEreadonly-strict-errors.ts:161ERR_READONLY_FIELD_REJECTEDregistry.ts:1292NAMESPACE_CONFLICTregistry.ts:1404DUPLICATE_ARTIFACT_OBJECT_NAMEregistry.ts:1463OBJECT_OWNERSHIP_CONFLICTsummary-errors.ts:24ERR_SUMMARY_RECOMPUTEtransaction-errors.ts:26ERR_TRANSACTION_UNSUPPORTEDtransaction-errors.ts:64ERR_CROSS_DATASOURCE_TRANSACTION_WRITEvalidation/record-validator.ts:136VALIDATION_FAILEDtenancy/system-write-organization.tsERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED— discharged by #16156Why this is worth a card rather than a shrug
The reasoning is the one #14936 established, and it applies unchanged to each row:
@objectstack/objectqldeclares both realms in its ownexports(importtodist/index.mjs,requiretodist/index.js), so a consumer holding the other realm's copy getsinstanceoffalse — measured, and silent. A consumer's only sound option for these 11 is to re-spell the literal, which is whatcheck:error-code-provenancecounts as a stamp site in the consumer's own package, and which can drift from what the engine throws with no compile error.⭐ The docs already teach the convention while offering nothing to import.
content/docs/kernel/contracts/data-engine.mdx:361says, ofReadonlyFieldRejectedError:and
ERR_READONLY_FIELD_REJECTEDis row 3 above — an inline literal with no exported constant. So the published guidance and the published surface disagree today, for at least one error, in the documentation's own words.Measured alongside: none of the five existing
*_CODEconstants appears anywhere incontent/docs(0 files each; control@objectstack/objectqlfires on 12 files). So no page enumerates these, and no page becomes stale by adding one.Suggested shape, for triage rather than a ruling
Per-error convergence onto the existing five-precedent shape —
export const X_CODE = '…' as const;with the class field reading from it, re-exported fromindex.ts. ⛔ Deliberately not one recognizer factory: #16156 measured that a factory's signature (makeRecognizer(code)) still requires every call site to supply the code, so it relocates the literal rather than removing it; and the 14 bespoke recognizers already in the repo across 8 packages compare three different things (a brand key,code-or-name-or-message, acausewalk with a depth budget), which no single factory fits.minorchangeset — which is why this is a sweep to be scheduled and graded, not a rider on any one PR. Splitting it per file (or per error family) is probably right.EMPTY_CREDENTIAL_REFUSAL_CODEis already'VALIDATION_ERROR'andrecord-validator.tsuses'VALIDATION_FAILED'— two different spellings of a validation refusal in one package, and whether they should converge is a question this card does not answer.Refs: #14936 · #16156 ·
content/docs/kernel/contracts/data-engine.mdx·packages/spec/scripts/check-error-code-provenance.tsGenerated by Claude Code