Repository navigation
Description
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.
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
os-zhuang commentedon Aug 14, 2026
Claiming this issue.
Session: https://claude.ai/code/session_012WMpuAfA2KSdDjGF6tm1bH
Branch:
claude/issue-8411-changeset-breaking-annotationScope: adding a breaking-change annotation to
.changeset/filter-formula-field-refusal.md's body only (bump staysminor). 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
os-zhuang commentedon Aug 14, 2026
{ "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:
.changeset/paths — a breaking changeset shipped locally-green and went red only in CI #8410 before posting anything there: it is already closed, fixed by PR fix(pm): read YAML block-scalar run: bodies in dispatch-gates discovery #8465, and that fix is already in this worktree's base commit (ea840ec7b2). [finding] dispatch-gates.mjs does not derive pr-automation.yml's Check Changeset for.changeset/paths — a breaking changeset shipped locally-green and went red only in CI #8410's own closing report documents the exact outcome I measured (.changeset/path derivingcheck-adr-0087-registration). My run is confirmation the landed fix works, not a discrepancy to correct — posting a "correction" there would misrepresent an already-closed, self-documenting issue. Did not post; flagged this back to you mid-task and am not acting further on it unless you want a confirmation-framed note added.skip-changesetlabel: not applied. This PR's entire payload is the changeset (an edit to an existing.changeset/*.md), not a tests/workflow/.claude/-only change that releases nothing — the opposite of what that label is for.check:empty-changesetreports the existing one as "declaring," not empty, and raised no objection.../objectstack-issue-8411) left in place pending your review; will tear down (rm -rf node_modules && git worktree remove) once you've enqueued or asked for changes.Generated by Claude Code
Generated by Claude Code
os-zhuang commentedon Aug 14, 2026
Landed —
ce681f7fonorigin/main(PR #8606, squash-merged). Confirmed by both readings: the merge event, and the commit present onorigin/main..changeset/filter-formula-field-refusal.mdnow carries the**BREAKING**annotation plus the ADR-0087 disposition marker it requires. The bump staysminor— 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()'sBREAKING_MARKERconstant is the literal**BREAKING**, which is whatmarkBreaking()prefixes.scripts/check-adr-0087-registration.mjs—breakingDeclaration()unions/\*\*BREAKING/iwith amajorbump and a conventional-commit!.They agree, and a live specimen was pulled from the
objectuisibling checkout to confirm the convention in practice.It was demonstrated, not asserted
check-adr-0087-registration.mjsbefore the edit: "this PR adds no declared-breaking changeset." After:✓ 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. AndhasBreakingAnnotation()was imported directly and run against both bodies —trueon the edited one,falseonorigin/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 Changesetwent 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: theskip-changesetlabel. ⛔ 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.mjsdoes 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
docs(changeset): annotate filter-formula-field-refusal.md as breaking (…