Skip to content

[finding] check-adr-0087-registration has no honest disposition for a published TYPE-surface narrowing — every category is refused, so the class is pushed to drop the **BREAKING** token instead #13080

Description

@os-litant

Met while writing the changeset for the #12104 shipping half (a @objectstack/client
return-type narrowing). Recorded rather than worked around in place: the workaround the gate
leaves available is the one that makes its own trigger less meaningful.

The dead end, measured

The changeset declared **BREAKING** — truthfully: narrowing a published Promise< any > to a
concrete type can stop a consumer compiling. check-adr-0087-registration then demands one of
its five dispositions, and every one is unavailable:

disposition why it does not apply
registered <id> The ADR-0087 registry feeds objectstack migrate meta, spec-changes.json and the generated upgrade guide. Nothing metadata-shaped moved — no Zod schema, no packages/spec declaration, no stored representation — so an entry would carry no prescription any of those three can project. It would be false data in the one ledger the gate exists to keep true.
not-required (unpublished) False. @objectstack/client publishes to npm.
not-required (already-registered <id>) False. No entry covers it.
not-required (no-migration-prescription) Refused by the gate, because the changeset carries a FROM/TO block: "A changeset that ships instructions for rewriting a consumer's code cannot also claim that no consumer has to rewrite anything."
not-required (runtime-interface-only …) The gate says outright it "inherits this same refusal (#8299)".

The refusal text names the intended exit — "if the prescription is genuinely for someone the
ledger does not serve, use a category that can be verified (unpublished,
already-registered)"
— and both of those are factually false here. So the exit the gate
offers is closed for exactly the case it describes.

What the class actually is

A published TYPE-surface narrowing: a consumer-facing prescription exists (rewrite
(await client.analytics.query(q)).rows as .data.rows), the affected party is a TypeScript
consumer, and the delivery channel is the compiler at their own call site. The ADR-0087 ledger
serves metadata upgraders and cannot reach them; the compile error already does, more precisely
than a release note.

The gate's no-migration-prescription refusal is not wrong in general — #6048 is exactly the
shape it was built to catch — it just cannot tell "a prescription for a METADATA upgrader" from
"a prescription for a source-code consumer", and only the first is the ledger's business.

Why this is worth a card rather than a shrug

The available move is to drop the **BREAKING** token, which is what the three prior cards of
this same class did (#8140, #11925, #12034 — all @objectstack/client return-type narrowings,
all minor, all recording the no-entry disposition in prose, none using the token). #12104
followed that precedent for consistency, and the consumer impact is stated in full either way.

But the cumulative effect is the concern: a whole recurring class of genuinely
compile-breaking change is nudged away from the repo's breaking-change marker, purely because
the marker routes it into a ledger it does not belong in. That erodes the token this gate
depends on, and the erosion is invisible — every gate stays green.

Two shapes worth weighing (not prescribed)

  1. A sixth category — e.g. not-required (type-surface-only <package>), verified the way
    unpublished is: the named package publishes, nothing under packages/spec changed in the
    diff, and no ADR-0087-shaped surface moved. Mechanically checkable from the same diff the
    gate already reads.
  2. Narrow the no-migration-prescription refusal to prescriptions aimed at the ledger's
    own audience, rather than firing on any FROM/TO block.

Either way, the class should be able to say **BREAKING** and still answer the gate honestly.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions