Skip to content

[finding] An accept-set narrowing owes a **BREAKING** banner in core but not in platform-objects — and the ADR-0087 classifier reads the banner #16421

Description

@claude

Two contract reviews in the same round, on PRs of the same shape (an accept-set narrowing shipped as minor), reached opposite answers about whether the changeset owes a **BREAKING** banner — each by correctly following the nearest precedent in its own package. The packages have drifted, and nothing in the tree reconciles them.

The two readings, both measured

@objectstack/platform-objects — no banner. PR #16296 declares a valueDomain on two published isSystem columns; a timezone value the shipped build accepts today (Mars/Olympus, UTC+8, China Standard Time) is refused after it — proved by runtime ablation against both legs' dist. Its changeset says "A NON-BREAKING ADDITION" and carries no banner. Precedent #14238 (6acb37eb9, .changeset/org-hierarchy-timezone-columns.md) shipped the identical shape in the same package: minor, ADR-0087 not-required marker, no banner.

@objectstack/core — banner mandatory. PR #16363 makes kernel.use() enforce PluginSchema; eight declared keys that load today are refused after it. Precedent d8024f0 — narrowing the same Plugin.type key — opens its changeset with:

**BREAKING** accept-set narrowing on a published type, shipped as minor under the repo's launch-window convention for breaking changes.

4635f3e (HotReloadConfig.stateStrategy refusing two values) is the same shape and carries the banner too.

Why the rules as written do not decide it

AGENTS.md:1021-1022 defines the trigger narrowly:

Breaking changesets must carry their migration. If the change removes or renames anything an author can write (a spec key, an export, a config field), the changeset body must state the FROM → TO mapping and the one-line fix

A value-set narrowing removes nothing and renames nothing — no key, no export, no config field disappears. By that text platform-objects is right. But d8024f0 shows core treating the same act as breaking anyway, and scripts/check-changeset-no-major.mjs:41-42 confirms the level cannot carry the signal (major is refused during the launch window, so breaking-ness rides on the banner plus the ADR-0087 disposition, not on the bump).

⇒ The banner is the only channel for "this narrows an accept set", and whether a narrowing must use it is decided today by which package you happen to be in.

Why this is worth a card rather than a per-PR judgement

⛔ It is not a defect in either PR — each followed its own package's precedent, and both were reviewed and verified on that basis.

⚠️ The cost is that check-adr-0087-registration classifies from the banner. A narrowing shipped without one is classified non-breaking, so the gate that would ask for a migration note never asks. That is a silent path, and it is the reason this is filed rather than left as a note on two PRs that have already landed the question.

What a fix would look like (⛔ not a recommendation — the options are the deliverable)

  • A — extend the written definition. Amend AGENTS.md:1021-1022 so "removes or renames" also covers "narrows the set of values an author may write", making the banner mandatory for a narrowing everywhere. Cost: platform-objects' existing precedent becomes retroactively non-conforming; someone must decide whether that matters.
  • B — keep the narrow definition and make core's practice explicit as a stricter local convention. Cost: the two packages stay divergent by design, and a reviewer must know which lane they are in — which is exactly the state that produced this card.
  • C — make the classifier stop depending on prose. Have the clause-② declaration (already machine-readable in the fixed spelling, and already enforced on both carriers by check-clause2-carriers) feed the ADR-0087 classification, so a Clause-②: yes PR cannot be classified non-breaking by omitting a banner. Cost: couples two gates that are independent today.

Re-check commands

git show d8024f0:.changeset/core-plugin-type-closed-set.md | head -20     # the BREAKING banner
git show 6acb37eb9:.changeset/org-hierarchy-timezone-columns.md           # same shape, no banner
sed -n '1021,1022p' AGENTS.md                                            # the written definition
sed -n '41,42p' scripts/check-changeset-no-major.mjs                     # why the level cannot carry it

Sources: contract reviews on PR #16296 (comment 5562601809) and PR #16363, both this round. Related: #14238, #16049, #15872.


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