Skip to content

[finding] one objectql package publishes both VALIDATION_ERROR and VALIDATION_FAILED as importable constants, and no published sentence says which applies when #16440

Description

@claude

Split out of #16159 by the round that closes it. That card's last row (VALIDATION_FAILED, PR pending) carried this question in its body — "whether they should converge is a question this card does not answer" — and closing the card would leave the question with no home. ⛔ Observation, not a runtime defect: nothing on main is wrong today. No assignee, no pm:queue; triage grades it.

The two spellings, measured on origin/main ac76425f0

One package, two exported constants, two different wire codes for what both call a validation refusal:

producer constant wire code
packages/objectql/src/secret-fields.ts:103 EMPTY_CREDENTIAL_REFUSAL_CODE VALIDATION_ERROR
packages/objectql/src/validation/record-validator.ts VALIDATION_FAILED_CODE (the #16159 conversion) VALIDATION_FAILED

Both are registered in ERROR_CODE_LEDGER. VALIDATION_FAILED is listed under @objectstack/objectql's own owner key; the ledger's prose distinguishes them repeatedly, and several of its comments say a given code is explicitly "Not a VALIDATION_ERROR synonym" — so the ledger already treats the two as carrying different meanings, in its own words, at least in the places where somebody wrote that sentence down.

Why this is a card rather than a shrug

The #16159 conversion made both spellings importable, which is the point at which the divergence stops being an implementation detail and becomes a published choice a consumer has to read. A consumer catching a refusal from this one package now has two constants to choose between and no published sentence saying which applies when.

⚠️ The conversion did not decide it, deliberately: the constant holds text byte-identical to the literal it replaced, so the decision is exactly as open as it was, and a pin test in that PR asserts the two are still two so that a future ruling has to argue for itself rather than arrive as a side effect of an unrelated edit.

What a ruling would cost, so triage can grade it

⛔ A convergence is a breaking rename of a registered wire code, in either direction, not a tidy-up:

  • both codes are in ERROR_CODE_LEDGER and are therefore in the population check:error-code-provenance reconciles;
  • VALIDATION_FAILED is re-authored as an inline literal at 148 non-test sites across 33 files in this repo — most of them independent producers minting their own house-code envelope rather than consumers of the engine class, but four of them are genuine recognizers (packages/types/src/validation-failure.ts, packages/rest/src/error-response.ts twice, packages/plugins/plugin-auth/src/objectql-adapter.ts);
  • scripts/check-changeset-no-major.mjs makes the **BREAKING** banner and the ADR-0087 disposition mandatory carriers for an accept-set change, so the ruling arrives with paperwork attached.

Three readings this card is asking for, ⛔ not a recommendation

  1. Leave them. They may genuinely name different refusals — an empty credential write is not a field-validation failure — in which case the fix is a published sentence saying so, not a rename.
  2. Converge on one, with the breaking-rename paperwork and a migration note.
  3. Rename neither but document the boundary in the ledger and in content/docs/kernel/contracts/data-engine.mdx, which is the page that already teaches catching engine refusals by code.

Refs: #16159 · packages/spec/src/api/error-code-ledger.zod.ts · packages/objectql/src/secret-fields.ts · packages/objectql/src/validation/record-validator.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

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions