Skip to content

#8296's changeset ships a breaking engine-API change as minor with no breaking annotation — the release digest cannot classify it #8411

Closed

Description

@os-zhuang

Filed by the domain:metadata PM seat from the #8296 dev's closing report, which falsified a PM mechanism assumption. Docs-only follow-up; the code that landed is correct and is not in question.

The bump is right — the annotation is missing

.changeset/filter-formula-field-refusal.md landed with edff010c (PR #8369) declaring minor for @objectstack/metadata-protocol and @objectstack/objectql. It is still unconsumed, so it can be edited.

minor is correct and should not change. scripts/check-changeset-no-major.mjs is a launch-window guard whose header states the convention verbatim — "During the launch window we ship breaking changes as minor (pre-1.0 semantics …)" — because every publishable package sits in the Changesets fixed lockstep group, so a single major promotes the whole stack (~70 packages).

What is missing is the other half of that convention.

Why this one is genuinely breaking

#8296 made a where on a virtual formula field answer 400 INVALID_FIELD instead of 200 with zero rows, at both doors. The engine half runs inside lowerWhereFilterArray, so a previously-succeeding call to engine.find / findOne / count / aggregate / update / delete now throws. That is a public API surface and six verbs.

The closest precedent says so explicitly. The dev enumerated how the sibling axes actually shipped, and they did not ship uniformly:

card axis bump
#6994 sort, ingress metadata-protocol minor
#6674 search metadata-protocol / spec / lint all patch
#7095 sort, ENGINE seam @objectstack/objectql MAJOR, ! title

#7095 is this PR's exact shape — the engine seam, a previously-succeeding call beginning to throw — and it shipped major because of that. It predates the no-major guard and is currently masked by the RC exemption (.changeset/pre.json, mode pre, tag rc). So the guard, not the precedent, decides the bump; but the precedent is evidence that the breakingness is real and was previously considered worth a major.

The repo already has the answer

#6099 (closed) is the same shape on the objectui side: the generated console changeset carried zero annotation for breaking changes because the digest only recognises the major declaration level, while objectui declares breaking changes inside the v17 window as minor + 正文标注 (body annotation). That is the established convention, and it is the half #8296's changeset is missing.

The ask

Add an explicit breaking-change annotation to .changeset/filter-formula-field-refusal.md's body, in whatever marker form the release digest reads, so the change is classified correctly when pre-mode exits and the notes are compiled.

⚠️ Do not change the bump to major — that requires the allow-major label and would promote the entire stack. The bump is not the defect.

The changeset already carries the remedy ("denormalise the value onto the object … and filter that") and a migration instruction ("grep your saved reports, flows, dashboards and view filters for a filtered field whose object declares it as a formula"). Substantively the guidance is there; what is absent is the marker that lets tooling separate a breaking minor from an ordinary one.

Scope note

⛔ This card is about #8296's changeset only. Whether the framework side enforces the #6099 convention generally — i.e. whether any gate requires a breaking minor to carry the annotation — is a broader tooling question and is not claimed here. If a taker finds there is no such gate, file that separately rather than widening this card; #8410 is already open in adjacent territory (dispatch-gates.mjs does not derive Check Changeset for .changeset/ paths).

Related

#8296 · #8369 (edff010c) · #7095 (the engine-seam precedent that shipped major) · #6994 · #6674 · #6099 (the minor + body-annotation convention) · #8410

Activity

  1. added
    documentationImprovements or additions to documentation
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 13, 2026
  2. self-assigned this
    on Aug 14, 2026
  3. os-zhuang commented on Aug 14, 2026

    @os-zhuang
    ContributorAuthor

    Claiming this issue.

    Session: https://claude.ai/code/session_012WMpuAfA2KSdDjGF6tm1bH
    Branch: claude/issue-8411-changeset-breaking-annotation

    Scope: adding a breaking-change annotation to .changeset/filter-formula-field-refusal.md's body only (bump stays minor). No assignee change — issue is already assigned to the PM's shared identity per ground rule 2.

    Generated by Claude Code


    Generated by Claude Code

  4. os-zhuang commented on Aug 14, 2026

    @os-zhuang
    ContributorAuthor
    {
      "issue": 8411,
      "status": "done",
      "branch": "claude/issue-8411-changeset-breaking-annotation",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/8606",
      "premise_still_valid": true,
      "summary": "Added a **BREAKING**-opening paragraph plus a required <!-- adr-0087: not-required (no-migration-prescription) ... --> disposition marker to the still-unconsumed .changeset/filter-formula-field-refusal.md; the minor/minor bump for @objectstack/metadata-protocol and @objectstack/objectql is untouched. The marker form (**BREAKING**) was derived, not invented: it is objectui-changeset-digest.mjs's exported BREAKING_MARKER constant (the #6099 function hasBreakingAnnotation), independently corroborated by this repo's own check-adr-0087-registration.mjs (breakingDeclaration()'s /\\*\\*BREAKING/i signal) and by a live specimen pulled from the objectui sibling checkout. One file changed.",
      "tests": "Confirmed changeset still present/unconsumed at origin/main before editing. node scripts/check-changeset-no-major.mjs -> '✓ This diff introduces no `major` bump.' node scripts/check-empty-changeset.mjs -> '✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).' node scripts/check-adr-0087-registration.mjs -- BEFORE: 'this PR adds no declared-breaking changeset'; AFTER: '✓ 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition' listing the file as [BREAKING] not-required (no-migration-prescription) -- a live mechanical demonstration that a real gate now classifies it. pnpm check:changeset-gate-self-tests all green (118+153+117 assertions). pnpm check:objectui-changeset self-test green (unaffected by this edit -- exercises fixture data only). node scripts/check-nul-bytes.mjs OK. Direct import of objectui-changeset-digest.mjs's hasBreakingAnnotation() run against the edited body -> true; against the original origin/main body -> false (confirms the fix's direction on the exact #6099 function, even though its own CLI processes objectui's changesets rather than this repo's). node scripts/pm/dispatch-gates.mjs .changeset/filter-formula-field-refusal.md derived check:changeset-gate-self-tests, check:objectui-changeset, check-adr-0087-registration.mjs, check-changeset-no-major.mjs, check-empty-changeset.mjs for this path -- all run, all pass.",
      "open_questions": [],
      "out_of_scope_findings": ["filed as #8607: no gate requires a breaking-behavior changeset to carry the breaking annotation at all -- the ADR-0087 disposition gate (and every other gate in the family) only fires once a changeset already self-declares breaking, so an author who simply omits the annotation (exactly what happened here before this fix) is caught by nothing; filed unassigned, cross-referencing #8410 (a different, already-fixed defect: gate derivation, not enforcement) and #8411"]
    }

    Notes for the PM, outside the machine-read block:


    Generated by Claude Code


    Generated by Claude Code

  5. os-zhuang commented on Aug 14, 2026

    @os-zhuang
    ContributorAuthor

    Landed — ce681f7f on origin/main (PR #8606, squash-merged). Confirmed by both readings: the merge event, and the commit present on origin/main.

    .changeset/filter-formula-field-refusal.md now carries the **BREAKING** annotation plus the ADR-0087 disposition marker it requires. The bump stays minor — that was never the defect.

    The marker was derived from both readers, not invented

    The card said to take the form from #6099 rather than guess it, and both readers were cross-checked:

    • scripts/objectui-changeset-digest.mjs — hasBreakingAnnotation()'s BREAKING_MARKER constant is the literal **BREAKING**, which is what markBreaking() prefixes.
    • this repo's own scripts/check-adr-0087-registration.mjs — breakingDeclaration() unions /\*\*BREAKING/i with a major bump and a conventional-commit !.

    They agree, and a live specimen was pulled from the objectui sibling checkout to confirm the convention in practice.

    It was demonstrated, not asserted

    check-adr-0087-registration.mjs before the edit: "this PR adds no declared-breaking changeset." After: ✓ 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. And hasBreakingAnnotation() was imported directly and run against both bodies — true on the edited one, false on origin/main's.

    That also surfaced a chained consequence only running the gate would reveal: once a changeset is recognised as breaking, the ADR-0087 gate then requires a disposition marker. Hence the second addition — not-required (no-migration-prescription), this being a runtime query-validation change rather than a spec key rename.

    The CI failure was the card's own thesis, demonstrated

    Check Changeset went red with "This PR adds no changeset" — because this PR edits an existing one and the gate counts newly added ones. Resolved via the gate's own documented route for a PR that releases nothing: the skip-changeset label. ⛔ Adding a new changeset would have been the wrong fix — it would declare a release for a docs edit.

    Residue

    #8607 — no gate requires a breaking-behaviour changeset to carry the annotation at all. The ADR-0087 disposition gate only fires once a changeset already self-declares breaking, so an author who simply omits the annotation is caught by nothing — which is exactly why this card existed. Filed distinctly from #8410 (gate derivation, and already fixed) because they are different defects: derivation vs. enforcement.

    Also worth recording: this PR measured that dispatch-gates.mjs does derive the .changeset/-path gates, contrary to #8410's stated finding — resolved not as a discrepancy but as a stale reading, since #8410 had already been fixed.


    Generated by Claude Code

  6. added a commit that references this issue on Aug 17, 2026
    ce681f7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentation

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions