Skip to content

objectui-changeset-digest misses a BREAKING annotation whose heading opens with a warning sign, so the console changeset under-counts breaking entries and can skip the ADR-0087 disposition #23112

Closed

Description

@objectstack-fleet

Filing-gate category ① — a product defect with a named landing point; reach: exception release text (the digest writes text that publishes into the @objectstack/console CHANGELOG).

Reader who acts: the domain:devx execution seat, and every later console pin hop (each runs scripts/bump-objectui.sh, which calls this digest). Found by the os-dev on #23104 (PR #23110), which hand-corrected this hop's changeset; the next hop gets no such correction unless the digest is fixed.

Dedupe: semantic issue search on this repo, closed included, words "objectui changeset digest misses BREAKING annotation heading hasBreakingAnnotation console changeset breaking marker" — 25 hits, none covers this shape (nearest: #6099, the original annotation support, closed; #7044, a frontmatter parser miss, closed).

Defect

hasBreakingAnnotation() in scripts/objectui-changeset-digest.mjs decides whether an objectui changeset is declared breaking by its body annotation. Its block-opener rule (BREAKING_BLOCK_OPENER, a regex on the line start) admits an optional heading mark, an optional bullet and optional emphasis before the word breaking, and nothing else. objectui#12242's changeset opens its annotation with a warning sign between the heading mark and the word, so the digest reads it as not breaking.

Reproduction (objectstack main @ 36b2889c, objectui 1fb1ced36243)

// node --input-type=module, from the objectstack repo root
import { hasBreakingAnnotation } from './scripts/objectui-changeset-digest.mjs';
// body = git -C ../objectui show 1fb1ced36243:.changeset/12242-types-field-metadata-family.md
hasBreakingAnnotation(body);                                          // false
hasBreakingAnnotation(body.replace('## ⚠️ BREAKING', '## BREAKING'));  // true

The heading at line 28 of that changeset reads: ## ⚠️ BREAKING, priced as minor under the fixed group's version policy.

Consequence

  • Running the bump over 7578323a544e..1fb1ced36243 wrote the objectui#12242 @object-ui/types entry unmarked and the count line "1 of these carries a breaking change" (two are declared breaking). That text publishes as written unless someone hand-edits it, as PR chore(objectui): move the console pin to 1fb1ced36243 (objectui main tip, Half 1 of #23104) #23110 did.
  • A range whose ONLY breaking entry uses this heading shape would also produce no breaking count at all, and so skip the ADR-0087 disposition prompt the breaking path raises.

Fix lands in

scripts/objectui-changeset-digest.mjs (BREAKING_BLOCK_OPENER / hasBreakingAnnotation) plus a --self-test case for a heading that carries a leading emoji or symbol before the word. Whether to accept any non-word prefix or an enumerated set is the implementer's call against objectui's actual changeset corpus.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p2 · domain:devx · area:devpath, into pm:queue. The opener rule admits a decoration before the word, and stays a line-start rule

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-12T23:55Z. ⛔ Not a claim, ⛔ not a dispatch.

  2. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2 · domain:devx seat 1 · 2026-10-13T00:02Z
    Session: session_012zwsLMxQBwHUYJgPyrpEvx
    Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-23112-digest-breaking-decoration
    Worktree: objectstack-issue-23112
    Domain: domain:devx
    Seat: domain:devx#1
    File surface: scripts/objectui-changeset-digest.mjs (BREAKING_BLOCK_OPENER / hasBreakingAnnotation and its --self-test cases), and nothing else (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (the default judgment tier; dispatch-gates --tier on this surface: "no path-derived mandate", floor sonnet · default opus · ceiling fable)
    Clause-②: no
    Responsibility: scripts/objectui-changeset-digest.mjs's BREAKING_BLOCK_OPENER (on main 07bc34ac0, :450) produces the miss | no platform path covers it: the digest's count line is the only breaking signal the console changeset carries, and PR #23110 corrected this hop by hand | every console pin hop (scripts/bump-objectui.sh) reaches it; today objectui#12242's changeset is a live instance
    Thread-read: 6134888169
    Serial constraints cleared: no open PR touches scripts/objectui-changeset-digest.mjs or scripts/bump-objectui.sh (all 12 open PRs' file lists read at this act); #23104's PR #23110 (seat 2, the pin hop that found this) does not touch the digest. It is the digest's next consumer, so its hand-corrected changeset stays as it is.

  3. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 23112,
      "status": "done",
      "branch": "claude/issue-23112-digest-breaking-decoration",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/23117",
      "session": "session_012zwsLMxQBwHUYJgPyrpEvx — subagent, so the parent's id (the dispatch's PM session; the claim's Session line names the same id)",
      "premise_still_valid": true,
      "summary": "The block-opener rule in scripts/objectui-changeset-digest.mjs 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 U+FE0E/U+FE0F 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. OPENS_WITH_BREAKING_LABEL admits the same decoration so an author's decorated label is not restated; without it, the opener fix would have doubled the marker on 2 real changesets (a bounded in-place fix, see deviations). Corpus: a full objectui clone, every head, main at 5595aabca30b; 5678 changeset blobs and 704 block-head lines carrying the word. Exactly ONE decoration occurs, U+26A0 U+FE0F, in 4 placements: '## ⚠️ ' 18 blobs/14 paths, '⚠️ **' 22/11, '⚠️ ' 8/7, '**⚠️ ' 3/1, union 51/33. The base rule missed 47 blobs/30 paths; the fixed rule misses 0. False positives: 0 of the 144 prose block-head blobs are admitted, and 0 previously-flagged blobs are lost. Hop range 7578323a544e..1fb1ced36243: the count line goes from '⚠️ 1 of these carries a breaking change' to '⚠️ 2 of these carry a breaking change'; the two are objectui e8fc452c3 (.changeset/12254-envelope-draw-retire.md) and 10aba37d1 (.changeset/12242-types-field-metadata-family.md, now marked **BREAKING**). Mechanism assumptions: (1) holds on 21c86444e, :450 used at :494; (2) reproduced (false as-is, true with the sign removed), read from a scratch clone because the shared objectui checkout is shallow (50 commits) and lacks 1fb1ced36243; (3) measured as above. Re the choice between 'any non-word, non-space run' and an enumerated set: neither. ASCII punctuation before the word is a prose shape in the same corpus ('(' 39 blobs, '—' 5, a double quote 2), and enumerating the one sign would leave the next pictograph as the same miss, so the rule takes the pictograph class.",
      "tests": "All at HEAD ea30d08d6. (1) node scripts/objectui-changeset-digest.mjs --self-test: exit 0, 147 checks, 'objectui-changeset-digest --self-test: all checks passed'. The new battery '#23112: a DECORATED annotation — a pictograph before the word' has 16 cases, floor 16; roster floor 16 to 17. (2) Ablation, from the committed state, via scripts/ablation-replace.mjs in wrap mode (no build/dist: root script run from source). Leg 1 empties DECORATION: anchor 1 to 0, replacement 0 to 1, blob afaae0663b1a to 666daf47768c; 12 of 16 #23112 cases go red; the 4 that stay green are the plain control and 3 negatives; no other battery moves; self-test exit 1; restored, blob == HEAD afaae0663b1a, git diff HEAD empty. Leg 2 reverts only OPENS_WITH_BREAKING_LABEL: blob afaae0663b1a to ce2adddc0ea8; exactly the 2 renderer cases go red; restored, blob == HEAD, git diff HEAD empty. (3) Gates: 30 derived by dispatch-gates --commands on the real change set (identical to the dispatch list), all exit 0; pnpm check:pm-dispatch-gates ran detached, exit 0, '1833 cases pass'; --ran: '30 derived, 30 run, 0 NOT-MEASURED, 0 UNRUN'. Script-suite extras: pnpm check:objectui-bump exit 0 ('20 assertions across 5 cases'); pnpm check:changeset-gate-self-tests exit 0 (empty-changeset 170 incl. the four-carrier regex agreement, adr-0087 548, no-major 401). (4) Lint, a proven narrowing: the population is read from eslint.config.mjs (the **/*.{ts,...,mjs,cjs} object covers scripts/*.mjs); --format json gives files=1, errors=0, warnings=0; no config enables type-aware linting (parserOptions carry ecmaVersion/sourceType only), so the verdict on untouched files is invariant. (5) Control-byte self-scan of the changed file: 0 hits. (6) CI at report time on ea30d08d6: 40 check runs, 11 success, 11 skipped, 18 in_progress, 0 failure.",
      "mcp_calls": "0 — no MCP GitHub tool called (reads via gh api REST; writes via the scripts/pm relay)",
      "api_writes": "3 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (a) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft, #23117) + POST /repos/objectstack-ai/objectstack/issues/23117/assignees [zhuangjianguo], read back byte-identical (10809 bytes); (b) label-write = POST /repos/objectstack-ai/objectstack/issues/23117/labels [skip-changeset], read back size/m + skip-changeset; (c) this os-dev-report comment = POST /repos/objectstack-ai/objectstack/issues/23112/comments. Plus git push (not REST) x3 to the claim branch.",
      "gates": {
        "derived": 30,
        "ran": 30,
        "exit_nonzero": 0,
        "reconcile": "30 derived, 30 run, 0 NOT-MEASURED, 0 UNRUN (dispatch-gates --ran, at ea30d08d6)",
        "extra": ["pnpm check:objectui-bump :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0"],
        "ci": "in_progress at report time (11 success, 11 skipped, 18 in_progress, 0 failure)"
      },
      "line_budget": "not applicable — no skills/** or line-ratcheted ledger touched; diff +188/-4 in 1 file (dispatch-gates changed-lines 192, under the 3000 human-merge threshold)",
      "deviations": [
        "File surface: also edited OPENS_WITH_BREAKING_LABEL in the same file, beyond the claim's parenthetical symbol list (BREAKING_BLOCK_OPENER / hasBreakingAnnotation / --self-test). All four bounded-fix conditions hold: same defect class, mechanical (the same fragment), same file and claim, same gate family. Required because the opener fix alone makes markBreaking restate a decorated summary label ('**BREAKING** — ⚠️ **BREAKING'), measured on 2 real changesets (10719-retire-phase35-validation-types.md, 7659-retire-validation-engine.md), which violates the self-test's existing 'never restated' contract. Named in the PR body with that evidence.",
        "objectui reads: the shared /home/user/objectui checkout is shallow (50 commits) and lacks 1fb1ced36243. The corpus and hop range were read from a separate blobless bare clone in this run's scratchpad, with the .changeset blobs batch-fetched. Nothing was fetched into, checked out in, or written to the shared objectui checkout.",
        "Report template: added gates / line_budget / deviations / files_changed, which the dispatch says the seat reads; the role file's template does not carry them.",
        "Attribution: commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The PR footer uses AGENTS.md's session-URL form."
      ],
      "files_changed": ["scripts/objectui-changeset-digest.mjs"],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: exception: release-text (the digest writes the @objectstack/console changeset that publishes into its CHANGELOG on every pin hop, scripts/bump-objectui.sh) · evidence: hasBreakingAnnotation reads as NOT breaking a parenthetical '(breaking …)' inside a bold lead label; 39 blobs / 23 paths in objectui's changeset history (full clone, main 5595aabca30b), e.g. objectui 8a55f0ccd .changeset/12059-sidebar-no-reorder.md, whose lead reads '**`@object-ui/layout` (breaking, declared `minor` per the version policy).**'. Reproduce: parseChangeset(text).body into hasBreakingAnnotation at objectstack ea30d08d6 returns false. Same family as #23112 (digest recall misses on author annotations) but a different shape, not at the line start, so it needs its own ruling against the positional rule; not fixed here · dedupe words: objectui changeset digest parenthetical breaking bold label hasBreakingAnnotation undercount"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT · PR #23117 (draft, head ea30d08d65) · domain:devx seat 1 · session_012zwsLMxQBwHUYJgPyrpEvx · 2026-10-13T00:43Z

    Checked on GitHub and on the branch, not on the report's say-so.

    PR shape: open, draft, base main, body line 1 Fixes #23112, and no other closing keyword in the body. Assignee zhuangjianguo, labels size/m and skip-changeset, mergeable: true. skip-changeset is right: root scripts/ is not a published package.

    Files: exactly one, scripts/objectui-changeset-digest.mjs, +188 / −4. check-governed-merges --pr 23117 says NOT governed, so the PR lands through the queue as usual.

    The rule, read on the branch: a new DECORATION fragment, (?:\p{Extended_Pictographic}[\u{FE0E}\u{FE0F}]?[ \t]*)* under the u flag, sits in BREAKING_BLOCK_OPENER between the heading/bullet mark and the emphasis mark, and again after the emphasis mark. The rule stays anchored at ^, and it admits no letter, digit or ASCII punctuation. That matches triage's binding: a line-start rule, ⛔ not a match on "BREAKING" anywhere in the line.

    Spot check, run by the seat on the fragment as committed. Each case is shown as new verdict / old verdict:

    • ## ⚠️ BREAKING, … reads true / false;
    • ⚠️ **BREAKING (…)** and **⚠️ Breaking …** read true / false;
    • the plain ## BREAKING control reads true / true;
    • prose reads false / false: ⚠️ Upgrade note: … is breaking, ## ⚠️ … this is not breaking, (breaking, …), — breaking, ⚠️ Non-breaking, A ⚠️ breaking.

    Claim surface, revised in this act: the claim 6134971322 listed BREAKING_BLOCK_OPENER / hasBreakingAnnotation / --self-test in this one file. The PR also widens OPENS_WITH_BREAKING_LABEL in the same file, and this review adds that symbol to the claim's surface. It is part of this fix, not an adjacent defect. With the opener fixed, markBreaking would have restated an author's decorated label (**BREAKING** — ⚠️ **BREAKING) on 2 real changesets. The PR body names the change and the evidence, and the dev's ablation leg 2 shows exactly the 2 renderer cases go red without it.

    Evidence (report, spot-checked above):

    • self-test: 147 checks, with the new battery at 16 cases, floor 16 → 17;
    • two ablation legs, each restored to the HEAD blob;
    • gates: 30 derived and 30 run, all exit 0;
    • corpus: one decoration in objectui's history, U+26A0 U+FE0F, in 51 blobs / 33 paths; the base rule missed 47 / 30, the fixed rule misses 0, and the 144 prose block-head blobs give 0 false positives;
    • hop range 7578323a544e..1fb1ced36243: the count line goes from 1 breaking entry to 2. mcp_calls: 0.

    CI on ea30d08d65: 42 check runs; Lint & Repo Gates and TypeScript Type Check are success, and no run is red. One shard, Test Core (1/6), was still in progress at this review. The seat lands the PR once every check is green or an expected skip.

    Out-of-scope findings:

  5. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    LANDED: PR #23117 is merged through the merge queue (merge commit c9b5b24b0f). Read on origin/main by domain:devx seat 1, session_012zwsLMxQBwHUYJgPyrpEvx, at 2026-10-13T01:00Z.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:devxpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions