Skip to content

[finding] objectql's error-code constants are all on the batteries barrel only, while /core publishes the SchemaRegistry that raises three of them #16260

Description

@claude

Filed by the domain:engine execution seat while landing the first slice of #16159 (PR #16259). ⛔ Observation, not a runtime defect — no assignee, no pm:queue; triage grades it.

The observation

@objectstack/objectql declares two entry points: . (the batteries-included barrel, src/index.ts) and ./core (the lean ADR-0076 engine entry, src/core.ts). SchemaRegistry is exported from both.

Every error-code constant the package publishes is on . only. Read from the built declarations rather than from the source barrels, because at least one constant reaches index.d.ts without being spelled in index.ts:

constant dist/index.d.ts dist/core.d.ts
DUPLICATE_RECORD_CODE yes no
HOOK_TARGET_REBIND_ERROR_CODE yes no
HOOK_UNSCOPED_DATA_ACCESS_CODE yes no
MULTI_UPDATE_HOOK_KEY_DIVERGENCE_CODE yes no
EMPTY_CREDENTIAL_REFUSAL_CODE yes no
SYSTEM_WRITE_ORGANIZATION_REQUIRED_CODE yes no
NAMESPACE_CONFLICT_CODE (PR #16259) yes no
DUPLICATE_ARTIFACT_OBJECT_NAME_CODE (PR #16259) yes no
OBJECT_OWNERSHIP_CONFLICT_CODE (PR #16259) yes no
NAV_CONTRIBUTION_GROUP_MISSING yes yes

⭐ The last row is the firing control for that table and the precedent that makes the question live: one code constant IS on the lean entry, and core.ts carries a docblock saying why (os build reaches it from there, and it costs the entry nothing because it imports nothing).

Corroborating measurement from PR #16259's surface ablation: dist/core.d.ts holds 79 exported names before and after that PR adds three constants, while dist/index.d.ts goes 244 to 247.

Why it may matter

A @objectstack/objectql/core embedder holds SchemaRegistry, so it reaches installPackage and registerObject — the two verbs that raise NAMESPACE_CONFLICT, DUPLICATE_ARTIFACT_OBJECT_NAME and OBJECT_OWNERSHIP_CONFLICT. It can therefore catch those refusals but not import the constants that name them, leaving it to re-spell the literal: precisely the state #16159 exists to remove, and what check:error-code-provenance records as a stamp site in that consumer's own package.

⭐ The counter-argument is real, which is why this is a finding and not a card with a remedy attached: "export them from both" is a published-surface widening per entry point and a policy decision about all nine, not a mechanical repeat. The ADR-0076 D2 boundary ratchet governs protocol/plugin imports, so on its face it does not object to a bare string constant — but that reading has NOT been confirmed with the ratchet's owner, and core-boundary.ratchet.test.ts is where it would be settled.

Deliberately not answered here

Whether the remedy is (a) export the code constants from both entries, (b) leave /core as it is and say so in its header so the omission reads as a decision rather than an oversight, or (c) rule that /core consumers are out of scope for the by code, not instanceof convention. Each is a different statement about what the lean entry promises, and (b) is cheap and may be the whole answer.

Scope note

⛔ Not a blocker for #16159 and not inside its completion scope: that card is discharged when the remaining inline literals become constants, and this question would still be open afterwards for all nine.

Refs: #16159 · #16259 · ADR-0076 · packages/objectql/src/core.ts · packages/objectql/src/index.ts · packages/objectql/src/core-boundary.ratchet.test.ts


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions