Skip to content

fix(scripts): the objectui changeset digest reads a BREAKING annotation behind a warning sign - #23117

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-23112-digest-breaking-decoration
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-23112-digest-breaking-decoration

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23112

Clause-②: no

What changed

One file, scripts/objectui-changeset-digest.mjs:

  • BREAKING_BLOCK_OPENER now admits a decoration between the heading or bullet mark and the word. A decoration is a run of pictographic symbols (Unicode Extended_Pictographic), each with an optional variation selector (U+FE0E / U+FE0F). It can sit on either side of the emphasis mark: ## ⚠️ BREAKING, ⚠️ **BREAKING, **⚠️ Breaking. The rule is still anchored at the line start. It admits no letter, no digit and no ASCII punctuation, so it is not a "contains BREAKING anywhere" match. On a line with no pictograph, the new fragment can only match the empty string, so the rule behaves exactly as it did before.
  • OPENS_WITH_BREAKING_LABEL admits the same decoration. This is the test markBreaking uses so that an author's own label is not stamped a second time. Without this change, the opener fix would make the digest print a doubled marker; details under Bounded in-place fix below.
  • Self-test: a new battery, #23112: a DECORATED annotation — a pictograph before the word, with 16 cases and a floor of 16. The roster floor goes from 16 to 17.

Corpus reading: which decorations occur, and does the rule raise a false positive

Tree: a blobless clone of objectstack-ai/objectui covering every head (4121 refs, main at 5595aabca30b). Every blob ever written under .changeset/*.md (README excluded) was then fetched in full: 5678 blobs over 4810 paths. Each body was parsed with the digest's own parseChangeset and walked with hasBreakingAnnotation's own block-start logic. 704 block-head lines carry the word.

Distinct decorations in front of the word (after the heading or bullet mark): exactly one, U+26A0 WARNING SIGN + U+FE0F (⚠️), in four placements:

placement blobs paths
## ⚠️ BREAKING … (heading) 18 14
⚠️ **BREAKING … (before the emphasis mark) 22 11
⚠️ Breaking … (plain paragraph) 8 7
**⚠️ Breaking … (inside the emphasis mark) 3 1
union 51 33
  • Base rule (21c86444e): 47 of those 51 blobs (30 paths) read as not breaking. The other 4 were caught by an unrelated signal.
  • This rule: 0 missed.

False positives, over the same corpus:

  • Prose lines: a block-head line whose text before the word contains a letter or digit is prose (144 blobs / 108 paths). The new rule admits 0 of them.
  • Lost readings: blobs the base rule reads as breaking and this rule does not: 0.
  • Newly admitted lines: 16 distinct texts, and each one is a decorated declaration. Examples: ## ⚠️ BREAKING for a host that hands reference_to to the widgets directly, ⚠️ **BREAKING if your code hands a numeric primary key to any of these three:**, ⚠️ Breaking change (marked minor under this repo's version policy): the ….
  • Doubled markers in rendered summaries (**BREAKING** — ⚠️ **BREAKING …): 0 with this change. If only the opener were fixed, there would be 4 blobs / 2 paths (10719-retire-phase35-validation-types.md, 7659-retire-validation-engine.md).

Why a pictograph class rather than either option the card names:

  • Not "any non-word, non-space run": ASCII punctuation in front of the word is a prose shape in this same corpus. Counts: ( 39 blobs ((breaking, declared minor per the version policy)), — 5, " 2 (described under "Breaking for TypeScript readers" below), (" 1, (** 1. A code span such as a backticked identifier named breaking would also count. Admitting any of these trades the miss for false positives.
  • Not an enumerated ⚠️: that fixes the corpus, but the next author's 🚨 BREAKING would be the same miss again.
  • The pictograph class: it admits no ASCII and no letter, and it shows zero false positives on the corpus.

The hop range 7578323a544e..1fb1ced36243

Before (base 21c86444e):

→ 10 releasing changeset(s), 1 breaking (0 declared major, 1 annotated), 1 release-nothing, 2 commit(s) without a changeset
⚠️ 1 of these carries a breaking change: 1 by the author's own breaking annotation in the changeset body — …

After (ea30d08d6):

→ 10 releasing changeset(s), 2 breaking (0 declared major, 2 annotated), 1 release-nothing, 2 commit(s) without a changeset
⚠️ 2 of these carry a breaking change: 2 by the author's own breaking annotation in the changeset body — …

The two breaking entries:

  • objectui e8fc452c3, .changeset/12254-envelope-draw-retire.md: counted before and after.
  • objectui 10aba37d1, .changeset/12242-types-field-metadata-family.md: now counted, and its line now reads - **minor** — **BREAKING** — The exported field metadata types stop declaring ….

The artifact has no other diff. PR #23110's hand-corrected console changeset, scripts/bump-objectui.sh, .objectui-sha and every .changeset/ file are untouched.

Self-test cases (battery #23112, 16)

Direct, against the criterion and the renderer:

  1. objectui#12242's changeset body reads breaking: its verbatim heading ## ⚠️ BREAKING, priced as minor under the fixed group's version policy, with a guard that the fixture really carries U+26A0 U+FE0F.
  2. The plain-heading control (## BREAKING, …) still reads breaking.
  3. Decorated prose containing the word does not read breaking: ⚠️ Upgrade note: nothing in this change is breaking …, ## ⚠️ Read before upgrading — this is not breaking, ⚠️ Non-breaking cleanup; a groundbreaking refactor.
  4. The sign before the emphasis mark: objectui's own wrapped ⚠️ **BREAKING (scored minor per this repository's version-alignment label. Its emphasis run crosses a line, so only the opener can answer.
  5. The sign inside the emphasis mark: **⚠️ Breaking for consumers that read result.data.**.
  6. The sign opening a plain paragraph: ⚠️ Breaking in places, ….
  7. The sign without its variation selector: ## ⚠ BREAKING (U+26A0 alone).
  8. The class, not the one sign: - 🚨 BREAKING: behind a bullet.
  9. ASCII punctuation is not decoration. A code span naming an identifier called breaking, and a quotation ("Breaking for TypeScript readers", below, …), both stay prose.
  10. markBreaking keeps an author's decorated label verbatim, and still marks a decorated ordinary summary.

Through buildDigest, in a fixture repo of its own (so the #6099 counts stay pinned):

  1. Exactly the two decorated annotations are counted (3 releasing, 2 breaking, 2 by annotation). The decorated prose is not counted.
  2. The count line reads ⚠️ 2 of these carry a breaking change: 2 by the author's own breaking annotation.
  3. The heading-annotated (12242-shaped) entry gets the marker on its line.
  4. A summary that opens with a decorated label (10719-shaped) renders verbatim and is never restated. The case asserts that the entry is flagged before it reads the line, because an unflagged entry renders the same text.
  5. The decorated-prose entry is not marked.
  6. A range whose only breaking entry is the decorated heading still raises the ADR-0087 disposition question. This is the card's second consequence.

Ablation

Both legs ran from the committed ea30d08d6, through scripts/ablation-replace.mjs in wrap mode. In each leg the anchor hit 1 → 0 and the replacement 0 → 1. After each leg the tool confirmed that the blob equals HEAD and that git diff HEAD is empty.

  • Leg 1: DECORATION emptied, which puts both regexes back to the base shape. 12 of the 16 #23112 cases go red. The 4 that stay green are the plain-heading control and the three negatives. No other battery moves. Self-test exit 1.
  • Leg 2: only OPENS_WITH_BREAKING_LABEL reverted. Exactly the 2 renderer cases go red: 10 (markBreaking verbatim) and 14 (summary verbatim, never restated).

Bounded in-place fix: OPENS_WITH_BREAKING_LABEL

It sits in the same file and has the same defect class (a decoration not recognised as a breaking label). The change is mechanical: the same fragment, in the same position.

It is required by this change. Once decorated annotations are flagged, markBreaking would prefix **BREAKING** — to an author label that opens with ⚠️ **BREAKING. That breaks the self-test's existing "never restated" contract, and the corpus measurement above shows it would happen on 2 real changesets. No other symbol in the file changed.

Gates (at ea30d08d6)

  • Derived set: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, run on the actual change set, derives 30 commands, identical to the dispatch's 30. All 30 exit 0. pnpm check:pm-dispatch-gates ran detached: ✓ dispatch-gates self-test (fast tier): 1833 cases pass, exit 0.
  • Reconciliation: --ran reports 30 derived, 30 run, 0 NOT-MEASURED, 0 UNRUN.
  • Suites of the digest's consumers:
    • pnpm check:objectui-bump: bump-objectui self-test PASSED — 20 assertions across 5 cases.
    • pnpm check:changeset-gate-self-tests: check-empty-changeset 170 assertions, including the four-carrier entry-regex agreement; check-adr-0087-registration 548; check-changeset-no-major 401.
    • pnpm check:objectui-changeset, the digest self-test: 147 checks, all checks passed.
  • Lint, a proven narrowing:
    • The population is read from eslint.config.mjs: the **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} object covers scripts/*.mjs.
    • --format json reports 1 file, 0 errors, 0 warnings.
    • No config object enables type-aware linting (parserOptions carries only ecmaVersion / sourceType), so this diff cannot move the verdict on any file it does not touch.
    • The full pnpm lint run is CI's.
  • Changeset: none. Root scripts/ ships in no package's files[], so this PR takes skip-changeset.

Acceptance notes

  • Out of scope, reported for the seat to triage, not fixed here. A parenthetical (breaking …) inside a bold lead label is not read as an annotation: 39 blobs / 23 paths are unflagged. Example: objectui 8a55f0ccd, .changeset/12059-sidebar-no-reorder.md, whose lead label reads @object-ui/layout (breaking, declared minor per the version policy)., bolded. It is a different shape: the word is not at the line start, so it falls outside triage's binding line-start rule and needs its own ruling.
  • No pull, left untouched. BREAKING_EMPHASIS_LABEL, which is scanned anywhere in the body, does not admit a decoration inside the run when the run sits mid-paragraph (**⚠️ BREAKING**). The corpus has zero occurrences of that shape.

Generated by Claude Code

… a pictograph

objectui's changeset authors decorate the annotation with a warning sign
(`## ⚠️ BREAKING, priced as minor ...`), and the block-opener rule admitted
nothing between the heading or bullet mark and the word, so the digest
counted such an entry as not breaking. A decoration is now admitted: a run
of pictographic symbols, each with its optional variation selector, on
either side of the emphasis mark. The rule stays anchored at the line start
and admits no letter, digit or ASCII punctuation.

The summary-label test that keeps an author's own label from being
restated admits the same decoration, so a summary opening with
`⚠️ **BREAKING (...)**` renders verbatim rather than behind a second marker.

Claude-Session: https://claude.ai/code/session_012zwsLMxQBwHUYJgPyrpEvx
Co-authored-by: Claude <noreply@anthropic.com>
… shapes

The before-the-emphasis case now uses objectui's own wrapped label, whose
run crosses a line, so the emphasis-label scan cannot answer it and only the
block opener can. The verbatim-label case asserts the entry is flagged before
it reads the rendered line, and the variation-selector cases spell the code
points as escapes.

Claude-Session: https://claude.ai/code/session_012zwsLMxQBwHUYJgPyrpEvx
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 13, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 13, 2026 00:46
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 13, 2026 00:46
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit c9b5b24 Oct 13, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23112-digest-breaking-decoration branch October 13, 2026 00:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants