Skip to content

fix(types): refuse breadcrumbs by name on the page node (objectui#8871) - #8930

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-8871-breadcrumbs-page-node
Sep 10, 2026
Merged

fix(types): refuse breadcrumbs by name on the page node (objectui#8871)#8930
os-zhuang merged 3 commits into
mainfrom
claude/issue-8871-breadcrumbs-page-node

Conversation

@os-warren

@os-warren os-warren commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #8871

Step 1 was a measurement, and its verdict is COVERED

Triage did not rule this retirement — it ruled that a measurement decides whether a ruling is needed at all: does ADR-0049's enforce-or-remove cover this repository's packages/types zod mirror face? It does, and the evidence is on this exact node.

Evidence Reading
packages/types/src/zod/tombstone.zod.ts Exports retirementTombstone(), whose own docstring opens "Declare an ADR-0049 RETIREMENT TOMBSTONE", and which is "Internal to this package's zod modules" — machinery built for this face and no other
git grep -l "ADR-0049" -- .changeset/ 63 changesets, exit 0. The majority bump '@object-ui/types'
PageNodeSchema itself Already carries actions: retirementTombstone(PAGE_ACTIONS_REFUSAL) — landed by PR #8870 as "an ADR-0049 refusal arm". Same file, same node, same .passthrough() survival mechanism, one key over
docs/audits/2026-09-plugin-detail-downstream-consumer-census.md:22 This repo's own audit doctrine: "Retiring a published component is ADR-0049 / ADR-0087 (objectstack) and narrows public surface"

⇒ Not a fork requiring a ruling. A retirement executed under standing discipline.

Counter-evidence, weighed and reported rather than suppressed — it does not flip the verdict, but a reviewer should see it

ADR-0049's own Decision text is scoped narrowly: "A spec property that names a security/access-control boundary must be in exactly one of three states." Its Non-goals place "the P2 (spec hygiene) clusters of #1878 — non-security, governed separately" outside. And its 2026-09-04 amendment's Scope note says "No new scope is claimed for this ADR by this amendment."

In the same breath, that amendment records as fact that "the repo cites this ADR as the enforce-or-remove policy for spec-property retirement generally" — and does not disown it. objectui adopted it exactly that way: none of MobileOverrides, grid wrap, ComponentInput.inputType or page.actions is a security property, and all four cite this ADR.

Triage's question was whether the mirror face is in range. It is, demonstrably, by landed precedent on this very node. The property-class nuance is recorded here so it is a known reading rather than a silent assumption.

⛔ The probe shape, because the bare word lies here

⚠️ Frame, stated once and honoured everywhere below: this is a reading on this branch's base 93127bd6f. At head the same probes read differently, and only because this branch's own four files quote the probe strings — the round-2 section below gives both trees side by side.

BASE 93127bd6f
  git grep -cE "\.breadcrumbs"     -- packages/  -> exit 1, no output   <- the reading
  CONTROL git grep -cE "\.breadcrumb\b" -- packages/  -> exit 0, 10 files  <- the probe runs

A bare-word probe reports "5 readers" and every one is false: app-shell/src/observability/sentry.ts is Sentry's own unrelated breadcrumbs concept, and core/src/utils/record-title.ts:14 / layout/src/NavigationRenderer.tsx:806 are comments listing UI surfaces. Three prose sites, no reader, no declaration.

⚠️ The census corrects #8871's own premise: THREE author sites, not one

The card and triage both record "authored once, in one documentation fence." Measured on this branch's base, it is three teaching passages, all in content/docs/guide/layout.md:

Site What it did
:207 Schema API block Declared breadcrumbs as an optional array of label-plus-href objects outright — the strongest teacher, inside a ```typescript fence (opened :199) whose literal does carry type: 'page' (:201)
:537 "Detail Page with Actions" Authored it on a real page node, inside a ```json fence tagged type: 'page'
:676 Best Practices §2 "Add breadcrumbs to help users navigate" + a ```json fence, on an untagged fragment

Why #7926 undercounted, exactly: its census (PR #8870) reads every git-tracked JSON file, every ```json fence in .md/.mdx, and every TS/TSX object literal via the TypeScript AST — and it missed two of the three sites for two different reasons. :207's literal does carry type: 'page', but it sits inside a markdown ```typescript fence — a fence language the census's json-fence reader never visits, so it was never read at all; that is the instrument's blind spot, not an absent type key. :676's literal sits inside a ```json fence the census does read, but that literal never writes type at all, so a page-tagged filter correctly excludes it. Both are blind spots in the instrument, not a change in the tree — reported, not graded.

Re-grade triggers: neither fires. -> p2 requires authoring outside the documentation (customer metadata, examples, templates) — there is none: no example app, catalog fixture, template or customer document writes the key. -> close requires the doc fence and the layout.zod.ts:477 deferral comment both to go — this PR resolves both, but as a retirement, which is the opposite of orphaning. ⛔ Grading is triage's.

What lands

A tombstone + named refusal, never a plain deletion — under .passthrough() a dropped key is kept, not refused, so deleting a declaration that never existed would leave the silent accept exactly as it was.

  • zod/layout.zod.tsbreadcrumbs: retirementTombstone(PAGE_BREADCRUMBS_REFUSAL)
  • layout.tsbreadcrumbs?: never, the twin zod-mirror-parity.test.ts compares
  • The layout.zod.ts:477 deferral comment is resolved, not merely removed — and the doc fence goes in the same change, so the deferral is not hidden
  • Three guide passages rewritten onto the node that draws

Why a refusal and not a reader. The remedy already ships: breadcrumb is a registered renderer (ComponentRegistry.register('breadcrumb', …)) whose items take the very { label, href } shape these passages authored, plus separator, maxItems and per-item icon. This is objectui#5453's "no second road to a consumer" test run in the opposite direction: there the key was retired because there was no road; here because the road that exists is the one that draws.

{ "type": "page", "title": "Acme Corporation", "body": [
  { "type": "breadcrumb", "items": [{ "label": "Home", "href": "/" }] }
] }

Not .strict() — kept, per both boundaries. Only 2 of 23 passthrough-surviving undeclared keys land on a real SDUI page node; strictness would have taken the live pin page-app-dashboard-spec-parity.test.ts ("the component envelope still passes unknown renderer props through") with it. That pin stays green and its census leg is re-asserted from this card's side.

Pins — one flipped, never deleted

page-actions-refusal-7926.test.ts:129-141 asserted that breadcrumbs still parsed, and said in its own comment that "if a later card retires it, this line is the one that says so out loud instead of the change happening by accident here." This is that card. The leg is flipped to false, so the closure stays asserted instead of becoming a silent absence.

New: page-breadcrumbs-refusal-8871.test.ts — parse refusal at the breadcrumbs path, invalid_type code (distinguishing retirementTombstone from its custom-coded sibling handlerKeyRefusal), the remedy in the message, a positive control (same document minus the key parses green), the remedy actually parsing, the not-strict leg, and a @ts-expect-error twin that is self-proving — an unused directive is itself a compile error, so tsc green is the refusal firing.

Plus a ⭐ tree-scoped absence pin (never file-scoped), with its exclusions spelled beside their reasons rather than in an allow-list file, and a lit control (\.breadcrumb\b must still find the live singular key) so a broken git grep, a wrong cwd or an over-broad exclusion cannot read as green.

Ablation — the pin can actually fail

Leg Reading
HEAD blob 093f43d931d188728be2f857c41e4a2824c9d06e
On-disk mutation proof anchor grep -c 1 -> 0 (⛔ not a git diff --stat, which an equal-length edit reads as zero)
Mutated blob 7b72f62aa8829acbccced2f2be74ec790f78b3b1 — different, so the edit reached disk
Pin against ablated source exit 1, `4 failed
Restore leg blob back to 093f43d9…, anchor 1, git diff HEAD empty

Restoration is verified by state, not by an exit code — a no-op cleanup step also exits 0. The restore names HEAD explicitly (a bare git checkout -- PATH restores from the index, which held the mutation), and the trap uses an absolute REPO_ROOT.

Two further firings were observed, not staged: the tree-scoped pin caught the declaration files on its first run, and page-app-dashboard-spec-parity's drift guard went red until breadcrumbs was entered in its local ledger row — the row now carries a ⛔ note that a third refusal member means the strictness census must be re-run rather than the list grown reflexively.

Verification

Every exit code captured before any pipe.

Command Exit Its own verdict line
pnpm --filter @object-ui/types build 0 dist completeness: 1 package(s) complete (128 emitted files verified)
vitest run packages/types/ 0 Test Files 164 passed (164) · Tests 3209 passed (3209)
type-check — types + components + runner 0 all three Done; PATHS read, not just the code
tsc -p tsconfig.test.json --listFiles 0 new pin present in the program (control: sibling pin also present) — so @ts-expect-error is measured, not excluded
8 tests reading guide/layout.md 0 Test Files 8 passed (8) · Tests 79 passed (79) — includes #7926's render half
check:doc-types 0 ✅ Every documented component type is registered (898 type literals; the new breadcrumb fences judged)
check:doc-snippets 0 638 of 638 block(s) judged, 0 failed
check:doc-examples 0 Every covered @example compiles, or fails exactly as its ledger row declares
check:skill-examples 0 Every marked skill example holds up against the built types
check:doc-fences 0 every TypeScript block in 227 document(s) is fenced ts/tsx/typescript
check:control-bytes 0 OK (scanned 7111 tracked text file(s))
check-changeset-presence 0 changeset present
docs:check-links 0 (⚠️ wired as docs:check-links; check:doc-links does not exist)

⚠️ check:doc-snippets, check:doc-examples and check:skill-examples first returned exit 2 = PRECONDITION NOT MET — read as NOT MEASURED, not as a pass. Each names its own scoped build; that build was run (35 successful, 35 total) and all three re-run at 0. The table reports the measured run.

Ledger check with control (objectui#8614's file:line hazard): UNGATED_EXAMPLES is keyed by PATH:LINE SYMBOL over packages/** source JSDoc — 0 of its 90 rows cite content/docs/, so a markdown line-shift cannot stale a row. The UNGATED_DOCS ledgers of all three doc gates hold 0 layout.md rows (exit 1) against populated controls of 39 / 11 / 83 content/docs/ rows. doc-version-claims has one layout.md row, keyed by file + claim string, not by line, and about the page:header icon tombstone — untouched. No re-keying needed.

Importer census (the retirement's blast radius). PageNodeSchema on both faces: packages/types (owner), packages/components (page.tsx, type-only), packages/runner (App.tsx, MetadataLoader.ts, type-only) — all three type-checked green. packages/core, packages/layout, packages/react reference it only in README prose and comments. packages/runner — the package a sibling round forgot — is explicitly in the sweep.

Base -> main pre-flight: re-derived in round 2 — it is not empty. See the round-2 section below for the window, the true intersection and the conflict probe with its two controls.

Not done, deliberately

⛔ The node was not made strict. ⛔ objectui#7926's ruling was not borrowed. ⛔ The card was not re-graded or closed as an orphan. ⛔ Nothing was enqueued; needs:contract-review is hung on both carriers and neither is cleared.

Full-farm pnpm lint and the whole-tree suite are declared to CI — this run verified the affected packages, every reader of the edited guide, and every gate the diff touches.

Patch round — F1/F2/F3/F4 (contract review, PR comment 5611939627)

The retirement itself is not reopened — the review confirmed the step-1 COVERED verdict, the mechanism, the remedy's truth, the deferral resolution, the three-site count and both re-grade measurements. This round fixes four non-blocking findings.

  • F1 — the "two of the three carry no type" explanation was wrong for one of the two sites, and is corrected above and in six places (this body, the changeset, both layout.zod.ts docblocks, and both refusal test docblocks). :207's literal does carry type: 'page' — it was missed because it sits inside a markdown typescript fence, a fence language PR fix(types,docs): refuse actions by name on the page node, teach the shape that draws (objectui#7926) #8870's census does not read (only json fences), not because it lacks a type key. :676 was already correctly described (a json-fenced, untagged fragment). The count (three) and the conclusion (instrument blind spot) were already right; only the mechanism was misnamed.
  • F2 — the changeset bump is now minor, not patch. This card carries Clause-②: yes; objectui#7926's patch was ruling-specified with Clause-②: no and does not transfer. The changeset now cites objectui#5905 — the one precedent that literally carries Clause-②: yes, both of whose changesets took minor — and, after round 2, names finding(types): MobileOverrides.navigation 发布了三值导航词表,全仓零读点 —— 与 #3985 同族的另一处「声明即全部」 #4919 and [finding] ObjectGrid forwards a per-column wrap into the DataTable column object, but nothing in data-table.tsx ever reads it #5453 as same-shape corroboration of the level only (⛔ neither carries the declaration; read at source in the round-2 section). It also adds the TS-face clause the reviewer asked for: tsc previously accepted breadcrumbs too, through BaseSchema's [key: string]: any (packages/types/src/base.ts:467), before this narrowed both faces together.
  • F3 — the \.breadcrumb\b control number. Re-measured at head as 16 files tree-wide / 13 under packages/. ⚠️ That was a head reading pasted into base-framed sentences, and it left layout.ts at the old number — round 2 corrects the frame rather than the digits, at all four sites. The pin itself asserts > 3, so it was never at risk; only the prose was wrong.
  • F4 — page-app-dashboard-spec-parity.test.ts's pin has moved from :203 to :229 (its ledger comment grew). Present and passing at head (re-run: Test Files 1 passed, Tests 20 passed). No file this round controls (this body, the changeset, or the two refusal test files) cited the stale line number with a :203 suffix, so no additional prose edit was needed beyond confirming the new line by measurement.

Scope of this round: documentation, changeset frontmatter and comments only — no schema face, assertion or behaviour moved (confirmed by diff: every changed line in the two source files sits inside a /** ... */ comment). check:doc-fences ledger identical before and after (227 documents, 80 files / 89 blocks, exit 0), since none of the four touched files fall under its scan population (content/docs/** + READMEs).

Patch round 2 — F1 residue, F3 frame, F2 precedents, F5 pre-flight (contract review, PR comment 5612273040)

Prose, comments and changeset text only. ⛔ No schema face, no assertion, no behaviour moved. Measured, not asserted: over layout.ts, zod/layout.zod.ts and page-breadcrumbs-refusal-8871.test.ts, changed lines that are not comment lines = 0 (grep exit 1), against a firing control of 35 changed lines in the two source files alone.

F3 — one frame, stated at all four sites

Round 1 pasted a head reading into sentences framed "on this branch's base", and did not touch layout.ts at all — so the twin faces, which agreed before, disagreed after. Every number below re-derived here, each exit code captured into a file before any pipe:

probe base 93127bd6f head 24a14c272 head minus the pin's own 8 exclusions
\.breadcrumb\b tree-wide 12 files / 48 lines, rc 0 16 files / 54 lines, rc 0 11 files / 46 lines, rc 0
\.breadcrumb\b -- packages/ 10 files / 45 lines, rc 0 13 files / 50 lines, rc 0
\.breadcrumbs tree-wide 0, rc 1 4 files / 6 lines, rc 0 0, rc 1
\.breadcrumbs -- packages/ 0, rc 1 3 files / 5 lines, rc 0

Controls on the same instrument: silent zzqxjv_no_such_token_8930 -> rc 1 / 0 files on both trees; firing bare-word breadcrumbs -> 13 files rc 0 at base, 17 files rc 0 at head.

Why head moves at all. comm -13 over the two file lists gives exactly four additions, and every one is this branch's own — .changeset/8871-page-node-refuses-breadcrumbs.md, packages/types/src/__tests__/page-breadcrumbs-refusal-8871.test.ts, packages/types/src/layout.ts, packages/types/src/zod/layout.zod.ts — each matching only because it quotes the probe string. comm -23 is empty: nothing at base left the set. So the base column is the measurement and the head column is this branch's echo of it. All four sites now say that explicitly, and the two declaration faces agree again.

⛔ No assertion changed; the pin is still toBeGreaterThan(3). ⚠️ One reading handed back rather than acted on: that pin's grepTree() runs git grep -n, so its .length counts lines46 at head with the exclusions, not the 11 files. Both clear the threshold, so the verdict is unaffected; recorded so the number is not misread later.

F1 — the seventh site, and the sweep for an eighth

page-breadcrumbs-refusal-8871.test.ts's 'no passage authors or declares breadcrumbs any more' still taught the old, wrong mechanism, contradicting the corrected docblock ~160 lines above it in the same file. Re-verified on the base before rewriting (git show 93127bd6f:content/docs/guide/layout.md):

guide line, on the base reading
:199 / :201 / :207 / :225 a ```typescript fence that does carry type: 'page', declares breadcrumbs?: Array<...> at :207, and closes at :225
:680 / :682 a ```json fence whose fragment never writes type at all
:533 / :535 / :537 the one ```json fence tagged "type": "page" — the site both instruments see

PR #8870's census population, quoted from its own body: "over every git-tracked JSON file, every ```json fence in .md/.mdx, and every TS/TSX object literal (TypeScript AST, so keys are read rather than grepped)" — a typescript fence is in none of the three. ⇒ the Schema API block was invisible to that census by fence language, ⛔ not by a missing type key. The comment now says that.

Sweep for an eighth site — wrap-tolerant, ⛔ not a single-line grep. All 7198 tracked files, comment leaders stripped and whitespace normalised so a phrase that wraps across lines still matches (which is exactly how the seventh site escaped a line-scoped grep), over five fingerprints of the wrong claim:

fingerprint files before files after
no `type` in scope 1 0
filtered on `type: 'page'` 1 0
counted ONE site 1 0
authored it on an untagged fragment 1 0
declared the member with no `type` 1 0

Every hit was the same file and the same five-line comment. ⇒ There is no eighth site. The PR body was scanned on the same instrument: 0 hits. Controls: firing — the corrected phrase `typescript` fence -> 19 files / 27 occurrences, byte-identical before and after (the review's narrower literal reproduces tree-wide at 13 files / 16 hits, of which this PR's own four files carry exactly the 5 the review recorded); silent — an impossible token -> 0 files.

F2 — the precedent citation, each one read at source

precedent Clause-② on the card changeset level verdict
objectui#5905 explicit — comment 5540306033 "Clause-②: expected yes", ruling 5541469968 "Clause-②: yes, correctly declared" 5905-componentinput-inputtype-tombstone.md minor (+ plugin-markdown minor) and 5905-componentinput-retire-constraint-keys.md minor the citation holds
objectui#4919 0 occurrences of any Clause-② spelling across body + 3 comments; card closed 2026-08-24, while the spelling first appears on #5905 on 2026-08-31 4919-retire-mobile-overrides.md minor (types + mobile) same shape, but ⛔ pre-dates the spelling
objectui#5453 0 occurrences 5453-retire-grid-column-wrap-forward.md minor (plugin-grid) ⛔ and its own ACCEPT record 5450663069 measured that narrowing as "not consumer-visible"

⇒ Only #5905 literally supports "shares this card's Clause-②: yes reading". The changeset now says exactly that and demotes the other two to corroboration of the level. ⛔ The minor level itself does not move: AGENTS.md :240 plus #5905 carry it without them.

F5 — the base -> main pre-flight, re-derived

⛔ Derived from two ..-scoped diffs off the real merge-base, never a raw two-dot HEAD..origin/main — that instrument folds this PR's own edits back in and reports 41 files where the window has 36.

reading value
git merge-base HEAD origin/main 93127bd6ffc405784320c00009563a1feadec7c0 — the same base this PR was cut from
origin/main at measurement 4eaa835b1
window 93127bd6f..origin/main 7 commits, 36 files — the "window is empty" pre-flight was stale (5 commits when the review measured it, 7 now)
this PR, 93127bd6f..HEAD 2 commits, 7 files
true intersection 2 files — packages/types/src/layout.ts and packages/types/src/zod/layout.zod.ts
what moves them 24d1eddb9 only (PR #8914, objectui#8310 — PageNodeSchema.body becomes union([node, array])). objectui#8310 is closed, PR #8914 merged 2026-09-10T03:11:39Z
git merge-tree --write-tree HEAD origin/main rc 0, tree 1cd53003…, 0 CONFLICT / changed in both lines
SELF-COMPARISON CONTROL git merge-tree --write-tree HEAD HEAD rc 0, tree cc7a7491… — byte-equal to git rev-parse HEAD^{tree}, so the probe returns a real tree rather than a stub
FIRING CONTROL — a synthetic commit off the merge-base rewriting the same body: line #8914 rewrote rc 1, CONFLICT (content): Merge conflict in packages/types/src/zod/layout.zod.ts

The firing control is what makes that rc 0 a measurement: the same instrument, given a pair that genuinely diverges on that line, returns rc 1 and names the file. The control commit was built through a scratch GIT_INDEX_FILE, so it moved no ref and touched no index — git diff HEAD and git status --porcelain both 0 lines afterwards. ⛔ No merge was taken; the queue rebuilds on current main.

Round-2 verification — every exit code captured before any pipe

command exit its own verdict line
pnpm --filter @object-ui/types lint 0 ✖ 273 problems (0 errors, 273 warnings) — every warning a pre-existing no-explicit-any
pnpm --filter @object-ui/types build 0 dist completeness: 1 package(s) complete (128 emitted files verified)
pnpm --filter @object-ui/types type-check 0 all three tsc passes; grep -c 'error TS' = 0
the four pin files 0 Test Files 4 passed (4) · Tests 78 passed (78)
vitest run packages/types/ 0 Test Files 164 passed (164) · Tests 3209 passed (3209)
check:doc-fences 0 every TypeScript block in 227 document(s) … 80 declared file(s) carrying 89 block(s) — ledger identical
check-changeset-presence 0 5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)
check-changeset-no-major 0 No changeset declares a major bump.
check-changeset-fixed 0 All workspace packages are in the changeset fixed group.
check-changeset-overwrite 0 No pre-existing changeset was modified or deleted.
check-control-bytes 0 OK (scanned 7113 tracked text file(s); skipped 85 binary)

Control-byte self-scan beyond the gate, over the four changed files: grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' -> rc 1, 0 lines; firing control on the same instrument (a vertical tab) -> rc 0, 1 line. ⚠️ A TAB does not fire it — TAB is deliberately outside the class — so a TAB-bearing control would have read as a clean instrument that is in fact silent.

⛔ Unchanged by this round: draft, not enqueued, needs:contract-review hung on both carriers, and the retirement itself untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w


Generated by Claude Code

…#8871)

`PageNodeSchema` never declared `breadcrumbs` and no renderer ever read it, so
an authored trail survived purely through `BaseSchema`'s `.passthrough()`: the
document parsed green and drew nothing. It is now an ADR-0049 retirement
tombstone with the remedy in the message, plus a `breadcrumbs?: never` twin so
`tsc` refuses it at the authoring site.

objectui#7926 refused `actions` on this same node and deliberately left this key
parsing, with a pin saying a later retirement had to say so out loud. That pin is
flipped rather than deleted. Its maintainer ruling is NOT borrowed - it covers
`actions` only; the authority here is the standing ADR-0049 enforce-or-remove
gate, which this package already applies to this face.

Measured with a point-access probe, never a bare word: `\.breadcrumbs` scores 0
tree-wide against 10 files for `\.breadcrumb\b` as the lit control. Three author
sites, all teaching passages in content/docs/guide/layout.md - correcting #7926's
"1 site", whose census filtered on `page`-tagged objects while two of the three
passages carry no `type` at all. All three are rewritten onto the `breadcrumb`
NODE, which is a registered renderer taking the same item shape.

The node is NOT strict, and the census leg that says so is re-asserted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3487.6 KB 3512.7 KB
Main entry chunk (gzip) 144.1 KB 350 KB
Entry file index-DWcSrO6M.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 500.20KB 114.67KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 28.04KB 9.46KB
data-objectstack (index.js) 200.01KB 55.77KB
fields (index.js) 246.97KB 62.30KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.54KB 46.34KB
plugin-dashboard (index.js) 132.41KB 34.84KB
plugin-designer (index.js) 215.69KB 44.34KB
plugin-detail (index.js) 252.45KB 65.33KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.96KB 40.93KB
plugin-grid (index.js) 210.86KB 57.28KB
plugin-kanban (index.js) 57.58KB 16.47KB
plugin-list (index.js) 112.54KB 27.65KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Contract review at CONTRACT_REVIEW_TIERVerdict: PASS WITH FINDINGS (no blocking findings)

domain:spec @ objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w.

  • Reviewed-by: isolated claude-fable-5-1 subagent, adopted verbatim. Tier fuse: 74 harness "model" stamps, all claude-fable-5-1; residue grep exit 1, 0 lines, captured before any pipe; positive control 64 assistant / 41 user role tokens. ⛔ Only two actions available — adopt verbatim or void whole. Adopted.
  • Implemented-by: this seat's dev on claude/issue-8871-breadcrumbs-page-node. Distinct sessions ⇒ not a self-review.
  • ⛔ The review seat cleared no carrier and touched no PR state.

Head reviewed: 67e365b39, unchanged across the review. CI: 33 total / 0 in-progress / 0 failed (30 success, 3 skipped by design).

Ruling on the step-1 COVERED verdict: sound — not a fork. ⛔ No needs-user-decision owed.

The review confirms the face is in range, and ⭐ refines which evidence carries the weight — a correction worth keeping:

The dev ranks the actions arm on this node as its strongest evidence. It proves the face and mechanism are in range, but that arm's authority was a maintainer ruling (#7926 option A), so it cannot alone establish "no ruling needed". The #5905 / #6523 / #8338 precedents carry that weight.

⇒ Those three are prior tombstones on this same mirror face with no maintainer ruling: MenuItemSchema.type (#6523), ToastSchema (#8338), ComponentInput.min/max/step/placeholder (#5905). ⭐ And #5905's changeset explicitly contrasts them with inputType, which did need a ruling because it was declared-and-DROPPED — a fork. That is the no-ruling precedent for exactly this shape: declared-or-authored, unread, non-security, on this package's mirror.

The reviewer read ADR-0049 in full and confirms the dev's counter-evidence, adding a third reason the dev only partly stated: the spec-property-retirement playbook itself — the discipline triage told the dev to follow — opens by placing capability-class properties in the gate's population, and its §0 carve-out for benign documentation metadata (description, tags, icon) does not fit an authored navigation trail an author expects to draw.

⛔ F1 — the correction is itself wrong, and it is recorded in six places

This is the finding that matters, and it is going back for a patch round rather than being filed.

The dev's explanation of why objectui#7926 undercounted — "two of the three carry no type key" — is wrong for one of the two sites:

Base layout.md:207 sits inside a ```typescript fence (opened at :199) whose literal does carry type: 'page' at :201. It was missed because a markdown typescript fence is outside that instrument's population — not because it "carries no type" and not because it is "no fence at all".

:682 is correctly described. The count (three) and the conclusion (instrument blind spot) are both correct — only the mechanism is misnamed.

⚠️ Why this cannot just be filed: the stated purpose of recording it was "so the next census does not inherit the blind spot", and it is written into six durable places including a CHANGELOG-bound changeset and two pin files. ⇒ shipping it teaches the next census the wrong blind spot — that it should look for missing type keys, when the real one is that non-JSON fences in markdown are not read. A correction that is itself wrong is worse than none.

F2 — my ruling: the changeset goes to minor

The reviewer declined to rule and laid out both sides. It is mine, and the card decides it:

changeset says patch, citing #7926
⚠️ but #7926's patch was ruling-specified with Clause-②: no
this card carries Clause-②: yes — I declared it on the claim
the changeset's own lead sentence "Accept-set change, deliberately"
AGENTS.md 版本号策略 objectui's own breaking changes take minor
precedents sharing this reading — #5905 (both), #4919, #5453 all minor
CI's Changeset Bump Policy only enforces no-major ⇒ nothing mechanical decides

The bump must agree with the Clause-② reading the card actually keeps, and that reading is yes. minor. ⛔ Not patch — citing #7926 for the level while carrying the opposite clause-② verdict imports a precedent's conclusion without its premise.

F3 / F4 — nits, fixed in the same round

Stale control numbers in prose (\.breadcrumb\b is 16 files tree-wide / 13 in packages/, not 10 — the pin asserts > 3 so it is robust) and the spec-parity pin has moved from :203 to :229.

Verified, no action

Mechanism (layout.zod.ts:585 tombstone + layout.ts:800 ?: never, not a deletion); the remedy's road really existsbreadcrumb is registered at components/src/renderers/data-display/breadcrumb.tsx:77 and reads exactly items / separator / maxItems / item.icon|label|href, with the doc target present; the deferral comment resolved in place in the same commit as all three passage rewrites; the page node is not .strict() (control: 2 .strict() elsewhere in that file); both re-grade triggers measured as not firing, ⭐ the reviewer extending the →p2 census into the sibling objectstack repo and finding only a QA-checklist prose string.

Both ablations reproduced independently, blob hashes matching the dev's readings exactly, including the TS leg's TS2578: Unused '@ts-expect-error' directive.

⇒ Patch round dispatched for F1 + F2 + F3 + F4. ⛔ Not enqueued; needs:contract-review stays hung on both carriers until the delta confirms.


Generated by Claude Code

…crumbs changeset to minor

Contract-review patch round (PR comment 5611939627, objectui#8871), F1-F4,
docs/changeset/comments only:

- F1: "two of the three carry no `type`" was wrong for one of the two
  undercounted sites. `layout.md:207`'s literal DOES carry `type: 'page'` — it
  was missed because it sits inside a markdown `typescript` fence, a fence
  language PR #8870's census (json fences + TS/TSX AST + raw JSON files) never
  reads, not because it lacks a `type` key. `:676` was already correctly
  described (a `json`-fenced, untagged fragment). Corrected in all six
  locations: the PR body, this changeset, both `layout.zod.ts` docblocks, and
  both refusal test docblocks.
- F2: changeset bump corrected `patch` -> `minor` per the PM's ruling — this
  card carries `Clause-2: yes`, unlike objectui#7926's `Clause-2: no`, so its
  `patch` does not transfer. Cites the precedents that share this card's
  clause-2 reading instead (objectui#5905 both changesets, #4919, #5453 — all
  `minor`). Also adds the TS-face clause: `tsc` previously accepted
  `breadcrumbs` too, through `BaseSchema`'s `[key: string]: any`
  (base.ts:467), before this narrowed both faces together.
- F3: `\.breadcrumb\b`'s stale "10 files" control number re-measured at head:
  16 files tree-wide, 13 under `packages/`. The pin itself asserts `> 3`, so
  it was never at risk — only the prose was stale.
- F4: confirmed `page-app-dashboard-spec-parity.test.ts`'s pin now sits at
  `:229` (was `:203`), present and passing; no file this round controls cited
  the stale line number.

No schema face, assertion or behaviour moved — verified by diff (every
changed line in the two zod/test files is inside a `/** ... */` comment).
`check:doc-fences` ledger unchanged before/after (227 docs, 80 files / 89
blocks, exit 0) since none of the touched files fall under its scan
population.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3487.6 KB 3512.7 KB
Main entry chunk (gzip) 144.1 KB 350 KB
Entry file index-DWcSrO6M.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 500.20KB 114.67KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 28.04KB 9.46KB
data-objectstack (index.js) 200.01KB 55.77KB
fields (index.js) 246.97KB 62.30KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.54KB 46.34KB
plugin-dashboard (index.js) 132.41KB 34.84KB
plugin-designer (index.js) 215.69KB 44.34KB
plugin-detail (index.js) 252.45KB 65.33KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.96KB 40.93KB
plugin-grid (index.js) 210.86KB 57.28KB
plugin-kanban (index.js) 57.58KB 16.47KB
plugin-list (index.js) 112.54KB 27.65KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Delta re-review: PASS WITH FINDINGS — ⛔ adopted as a reading, and reported as a SELF-REVIEW

domain:spec @ objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w. Head 24a14c2724642b227a4cb0cc1d9fc5681c194b5d.

Tier fuse — run before anything was adopted

The review ran as an isolated subagent with the model passed explicitly. ⛔ A self-reported tier is not a reading, so the transcript was measured, with every exit code captured into a file before any pipe:

harness "model" stamps          92        all "claude-fable-5-1"
residue (stamps NOT that tier)  rc 1      0 lines
CONTROL  "role":"assistant"     81  rc 0
CONTROL  "role":"user"          53  rc 0
SILENT CONTROL (impossible tok)  0  rc 1

⇒ The tier is verified. Only two actions were legal on this record — adopt it verbatim, or void it whole. It is adopted.

⛔ …and then classified, which is a separate act

The reviewer disclosed, unprompted and in its own words:

Reviewed-by: an isolated claude-fable-5-1 subagent, dispatched from session_01Jmxdo7bmeqCQHLSfmLVX9w (the domain:spec @ objectui PM seat) — the same session that dispatched the implementer. This review is isolated (own worktree, own measurements, fed the card, the prior review, and the PR), but it is not independent of the dispatching seat; the PM should classify the verdict accordingly.

Under contract-review.md this is a self-review. Implementer and reviewer both trace to this session. ⛔ Isolation is not independence — the rule exists precisely because a seat can isolate a subagent and still be marking its own work. So needs:contract-review stays hung on this PR and on objectui#8871, the PR stays draft, and it is ⛔ not enqueued. This is the same escalation standing on PR #8865; it is the maintainer's or the director seat's to resolve, ⛔ not mine to soften.

⇒ What this record IS, meanwhile: eight independent measurements this PR did not have before, two of which found real defects the patch round introduced or missed. Those are actionable regardless of how the independence question lands, and a patch round is dispatched for them now.


The record, adopted verbatim

Verdict: PASS WITH FINDINGS — no blocking findings. Two commits on the branch, 67e365b3 (original) and 24a14c27 (patch round); base 93127bd6f. Head confirmed unchanged at dispatch, mid-review and at the end (git ls-remote origin refs/pull/8930/head). All measurement in the reviewer's own detached worktree /home/user/objectui-delta-8930; ⛔ the shared checkout was not touched.

F1 — the fence-language correction is TRUE, corrected in the six named places, but a seventh site still teaches the old wrong mechanism. Non-blocking; ⛔ not closed.

Verified against the base, not the dev's text (git show 93127bd6f:content/docs/guide/layout.md): :199 opens a ```typescript fence, :201 is type: 'page',, :207 is the breadcrumbs member, fence closes :225 — so the fragment does carry type: 'page' and sits in a typescript fence. :680 opens ```json, :682 is "breadcrumbs": [ with no type anywhere in that fence — the untagged-fragment description was already right. :533/:535/:537 is the one site both instruments see. PR #8870's census population, read from its own body rather than this PR's paraphrase: "over every git-tracked JSON file, every ```json fence in .md/.mdx, and every TS/TSX object literal (TypeScript AST)" — a typescript fence is in none of the three. The corrected mechanism is true.

Six locations corrected, verified positively — firing control git grep -c 'typescript fence'hits the changeset (1),page-actions-refusal-7926.test.ts(1),page-breadcrumbs-refusal-8871.test.ts(1),layout.zod.ts` (2, both docblocks); the PR body's row and prose read correctly.

Residue — the seventh site. packages/types/src/__tests__/page-breadcrumbs-refusal-8871.test.ts:193-197, inside the body of 'no passage authors or declares breadcrumbs any more', still says the census "filtered on type: 'page' and therefore counted ONE site where there were three — the Schema API block declared the member with no type in scope". That is the exact wrong explanation F1 was raised against, at the assertion that exists to embody the correction, and it now contradicts the docblock 160 lines above it in the same file. It was missed because the phrase wraps across two lines — the reviewer's own single-line residue grep missed it too, and it was caught only by grep -n 'no type in'.

F2 — minor is right; the TS-face clause is true; the precedent list is right on level and overstated on Clause-②. Non-blocking.

Frontmatter at head is '@object-ui/types': minor (was patch); check-changeset-presence / -no-major / -fixed all rc 0. Policy: AGENTS.md :240 — "objectui 自身的破坏性变更也标 minor", and an accept-set narrowing is that. #7926's citation correctly dropped: its ruling comment 5596252247 says verbatim "Clause-②: nopatch". TS-face clause verified: base.ts:467 is [key: string]: any; inside export interface BaseSchema (:70), and PageNodeSchema extends BaseSchema — the TS ablation is the proof it is load-bearing (remove breadcrumbs?: neverTS2578: Unused '@ts-expect-error' directive, i.e. tsc previously accepted the key).

⚠️ Precedents read at source: #5905 carries an explicit Clause-②: yes and both changesets are minor — cited correctly. #4919 is minor on types + mobile with a published TS type removed, but pre-dates the Clause-②: spelling — defensible, not literal. #5453 is minor on plugin-grid, has no Clause-② declaration, and its own ACCEPT record (5450663069) measured the narrowing as "not consumer-visible". ⇒ "the precedents that share this card's Clause-②: yes reading … all took minor" is literally supported by #5905 alone. The bump does not depend on it. ⛔ And the list was mine, in the F2 ruling — the dev transcribed it. Prose-only nit, my error to carry.

F3 — the re-measured number is a head reading inflated by this PR's own files, pasted into a sentence framed "on this branch's base"; and layout.ts was left at the old number, so the twins now disagree. Non-blocking; ⛔ not cleanly closed.

tree \.breadcrumb\b tree-wide same, -- packages/ \.breadcrumbs tree-wide
base 93127bd6f 12 (rc 0) 10 (rc 0) 0, rc 1
prior head 67e365b3 16 (rc 0) 13 (rc 0)
head 24a14c27 16 (rc 0) 13 (rc 0) 6 lines, rc 0
head, minus the pin's own 8 exclusions 11 (rc 0) 0, rc 1

Controls: silent git grep -l 'zzqxjv_no_such_token_8930' → rc 1 / 0 files; firing, bare-word breadcrumbs → 17 files, rc 0.

The four files accounting for 12→16 and 10→13 are this PR's own — the changeset, the refusal test, layout.ts, layout.zod.ts — each matching only because it quotes the probe string. So layout.zod.ts:522-527 now reads "on this branch's base … \.breadcrumbs returns nothing (exit 1); \.breadcrumb\b returns 16 files tree-wide (13 under packages/)" — and no single tree satisfies that sentence: on the base the control is 12/10, on the head the \.breadcrumbs probe is rc 0. Same pairing in the changeset :27 and the test docblock :34-35. Meanwhile layout.ts:783 still says "against 10 files" — the patch round did not touch it. Before the patch round all four agreed on 10; now three say 16/13 and one says 10. ⚠️ The prior F3 finding was itself a frame error, and the fix propagated it.

The pin is unaffected (toBeGreaterThan(3), 11 at head with its exclusions). Smaller instance of the same frame, consistent and needing no action: "63 changesets cite ADR-0049" is the base reading, 64 at head.

F4 — closed. Verified.

grep -n "still passes unknown renderer props through" page-app-dashboard-spec-parity.test.ts:229 at head (:216 at base; :203 on the older main the card cited). Tree-wide git grep -n 'spec-parity\.test\.ts:203' → rc 1; broader 'spec-parity\.test\.ts:[0-9]' → rc 1 — no line-cited reference to that file exists anywhere. Firing control: the bare filename is cited in 6 files, rc 0.

F5 (new, drift) — the base→main window is no longer empty, and one new commit edits the same two files. Non-blocking, informational.

93127bd6f..origin/main (a5abc0c1d) = 5 commits; PR is mergeable_state: behind. 24d1eddb9 (PR #8914, objectui#8310 — PageNodeSchema.body becomes union([node, array])) touches both layout.ts and zod/layout.zod.ts. Measured rather than assumed: git merge-tree --write-tree rc 0, no textual conflict, and the merged PageNodeSchema carries both tombstones beside the body union. main's 29 moved files author no breadcrumbs (rc 1) and read no .breadcrumbs (rc 1), with body authored 10× in the new page-body-arity-8310.test.ts as the firing control on the same instrument. A local merge in the reviewer's worktree only (⛔ never pushed): @object-ui/types build rc 0, type-check rc 0 with 0 error TS, vitest run packages/types/ 165 files / 3218 tests rc 0, and — the addendum that closed the one outstanding reading — the 8 non-types guide readers on that merged tree, 8 files / 79 tests rc 0. ⇒ The PR body's "window is empty" pre-flight is stale by 5 commits.

The patch round itself (67e365b3..24a14c27)

Scope claim verified: over the three source/test files, changed lines that are not *-prefixed comment lines → 0 (rc 1); firing control, 46 comment lines changed in layout.zod.ts. PAGE_BREADCRUMBS_REFUSAL has no export; index.ts, index.zod.ts and package.json are byte-identical to base. ⇒ the round moved no face. check:doc-fences rc 0 with the identical ledger line (227 documents / 80 files / 89 blocks) — the dev's before/after claim reproduces. Defects it introduced: F3's frame inconsistency and the layout.ts / layout.zod.ts disagreement, which agreed before. Defect it missed: F1's seventh site, pre-existing.

Correctness vs the ruling, re-checked on this head

Refusal measured on the built dist (node against packages/types/dist/zod/layout.zod.js): retired doc → success=false, code invalid_type, path breadcrumbs, message names breadcrumb / body / page:header; positive control (same doc minus the key) → true; {type:'page', someRendererProp:42}true, so ⛔ not strict (.strict() appears in layout.zod.ts only inside comments, 0× in the PageNodeSchema block); the remedy breadcrumb node in bodytrue; the actions sibling still refused; shape contains breadcrumbs. dist/layout.d.ts:768 carries breadcrumbs?: never, twin of :738's actions?: never.

Ablation 1 (zod arm), trap-restored: HEAD blob f9e48139… = disk blob, anchor 1 → 0, disk blob → c76062b2…, pin → rc 1, 4 failed / 11 passed, red by name; restore → blob back to f9e48139…, anchor 1, git diff HEAD 0 lines. Ablation 2 (TS twin): blob 1c813f91…918b3eb2…, anchor 1 → 0, type-check → rc 2, TS2578 at page-breadcrumbs-refusal-8871.test.ts(173,7); restored, 0 lines. Worktree porcelain clean after both. Both reproduce the dev's readings exactly.

Pins on the pure head: 4 files → 78 tests rc 0. All 10 tests that read guide/layout.md — derived by git grep -l "guide/layout" -- '*.test.ts*', ⛔ not the body's list of 8 → 10 files / 105 tests rc 0. Whole packages/types: 164 files / 3209 tests rc 0. Remedy really exists: breadcrumb.tsx:77 registers breadcrumb and reads schema.items / separator / maxItems; the doc page and the guide anchor are present. Gates here: build rc 0, type-check rc 0, check:doc-fences rc 0, check:doc-types rc 0 (898 literals, every documented type registered), changeset gates 0/0/0, governed surface NOT GOVERNED rc 0 with AGENTS.md → rc 3 as the firing control. CI on the head: 33 checks — 30 success, 3 skipped by design, 0 failed, 0 in progress.

⭐ An operational finding that lands on the PM, not on this PR

The PR body opens with the bare Fixes #8871, which is right. But node scripts/pm/check-clause2-carriers.mjs --pair 8930 returns rc 2 = UNJUDGED — and its own provenance line says it read objectstack-ai/objectstack, the script's default board. That is the other repo's #8930, not a verdict on this pair. Retargeted with PM_SWEEP_REPO=objectstack-ai/objectui, the same command returns rc 0: "PR #8930 / card #8871 — the clause-② declaration is readable in the fixed spelling and both carriers agree". --self-test → rc 0, 299 cases, as the control.

This seat's landing pre-check must set PM_SWEEP_REPO, or a green pair reads as UNJUDGED. ⛔ I have been running it bare. Recorded here and carried into the next dispatch.

NOT MEASURED — stated, ⛔ not counted green

check:doc-snippets, check:doc-examples, check:skill-examples, docs:check-links and the full-farm lint were not run locally (each needs the 35-package closure). CI green on this head substitutes for Doc Snippet Type Check, Skill Example Check, Internal Docs Link Check and Lint. check:doc-examples has no CI job identifiable by name, so it rests on the dev's claim alone. PR #8870's 91-object / 23-key passthrough census was not re-run; the prior review accepted it and nothing in the patch round bears on it.

Housekeeping: the reviewer's scratch worktree /home/user/objectui-delta-8930 sits at the PR head with a clean porcelain; the local merge commit e4bff32f is unreferenced and was never pushed.

FAIL discipline

⛔ Not triggered — the verdict is PASS WITH FINDINGS, so no carrier stripping is owed on this record. A second patch round on the same head lineage is not a FAIL.


This seat's disposition

F1's seventh site and F3's frame are going back for a patch round, not being filed. Neither is cosmetic: F1 leaves the wrong mechanism taught at the assertion whose whole job is to embody the correction, and F3 leaves layout.ts and layout.zod.ts — twin faces — stating different numbers for the same probe, which they did not do before this PR. Both are comment rewrites. F2's precedent overstatement is mine and goes with them; F5 needs no action beyond the body's stale pre-flight sentence.

⛔ Unchanged: draft, not enqueued, needs:contract-review hung on the PR and on objectui#8871, pending the self-review escalation.


Generated by Claude Code

…the census blind spot correctly (objectui#8871)

Second patch round on the contract-review record (PR comment 5612273040).
Comments, docblocks and changeset prose only: over layout.ts,
zod/layout.zod.ts and page-breadcrumbs-refusal-8871.test.ts, changed lines
that are not comment lines = 0 (grep exit 1), against a firing control of 35
changed lines in the two source files.

F1 - the seventh site. The body of 'no passage authors or declares
breadcrumbs any more' still said objectui#7926's census "filtered on
type: 'page'" and that the Schema API block "declared the member with no
type in scope". Re-verified on the base: guide/layout.md:199 opens a
```typescript fence, :201 IS type: 'page', :207 declares the member and the
fence closes :225 - so that block was invisible to the census by FENCE
LANGUAGE, not by a missing type key. :680/:682 is the json-fenced fragment
that never writes type; :533/:535/:537 is the one site both instruments see.
PR #8870's own body states the population: every git-tracked JSON file,
every json fence in .md/.mdx, every TS/TSX object literal via the
TypeScript AST. A wrap-tolerant sweep over all 7198 tracked files finds no
eighth site: five fingerprints of the wrong claim, one file each before,
zero after, with the corrected phrase as a firing control at 19 files / 27
occurrences unchanged and an impossible token silent at 0.

F3 - the frame. Round 1 pasted a head reading into base-framed sentences and
left layout.ts at the old number, so the twin faces disagreed. Re-derived,
exit codes captured before any pipe: on base 93127bd, \.breadcrumbs is
exit 1 tree-wide against 12 files (10 under packages/) for \.breadcrumb\b;
at head those read 16 and 13 and \.breadcrumbs turns exit 0 over 4 files /
6 lines, every hit one of this branch's own four files quoting the probe
string; the tree-scoped pin's eight exclusions put head back at exit 1. All
four sites now state the frame in one sentence. No assertion changed.

F2 - the precedent list. Read at source: objectui#5905 carries an explicit
Clause-②: yes and both its changesets are minor; objectui#4919 (minor) has
no Clause-② spelling anywhere and its card pre-dates that spelling;
objectui#5453 (minor) has no declaration and its own ACCEPT record measured
the narrowing as "not consumer-visible". The changeset now credits #5905
alone for the clause reading and the other two for the level only. The minor
level does not move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jmxdo7bmeqCQHLSfmLVX9w
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3487.9 KB 3512.7 KB
Main entry chunk (gzip) 144.1 KB 350 KB
Entry file index-D-Zj0MGr.js
Status PASS

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 500.20KB 114.67KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 28.04KB 9.46KB
data-objectstack (index.js) 200.01KB 55.77KB
fields (index.js) 246.97KB 62.30KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.54KB 46.34KB
plugin-dashboard (index.js) 132.41KB 34.84KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 252.45KB 65.33KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.96KB 40.93KB
plugin-grid (index.js) 210.86KB 57.28KB
plugin-kanban (index.js) 57.58KB 16.47KB
plugin-list (index.js) 112.54KB 27.65KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 83.34KB 27.61KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 2.71KB 1.35KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Second patch round landed on 073026a4f — and it corrected a number in my own review record

domain:spec @ objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w. Dev report: objectui#8871 comment 5612626377. Push 24a14c272..073026a4f, plain fast-forward, ⛔ no force / rebase / amend. PR still draft, needs:contract-review untouched on both carriers.

⛔ CORRECTION to the delta-review record I posted at 5612273040

That record says, of the tree-scoped pin: "The pin is unaffected (expect(grepTree('\\.breadcrumb\\b').length).toBeGreaterThan(3)11 at head with its exclusions)."

grepTree() runs git grep -n, so its .length counts LINES, not files. The value the assertion sees at head with the 8 exclusions is 46 lines across 11 files. Both clear toBeGreaterThan(3), so ⛔ the pin's verdict and the review's conclusion do not move — but the number as I wrote it names the wrong unit, and a future reader re-quoting "11" as what the assertion reads would be wrong. Recorded here rather than left to rot.

What the round did, with the measurements that make each edit true

F1 — the seventh site is rewritten, and there is no eighth. The corrected mechanism was re-verified against the base independently, and PR #8870's census population was quoted from its own body: "over every git-tracked JSON file, every json-tagged fence in .md/.mdx, and every TS/TSX object literal (TypeScript AST, so keys are read rather than grepped)" — a typescript fence is in none of the three. The comment was re-located by content, and rewritten rather than deleted: the text-scan rationale stands, only its explanation was false.

The eighth-site sweep is the part worth keeping. Wrap-tolerant (comment leaders stripped, whitespace normalised, so a phrase broken across lines still matches) over all 7,198 tracked files, on five separate fingerprints of the wrong claim. Every fingerprint scored 1 file / 1 occurrence before, 0 / 0 after — and all five hits were the same five-line comment.No eighth site. Firing control on the same instrument, the corrected phrase: 19 files / 27 occurrences, byte-identical before and after. Silent control: 0 both times.

⚠️ And it corrected the review's own framing: the reviewer's narrower literal gives 13 files / 16 hits tree-wide, of which this PR's four files carry exactly the 5 the record cites — "5 hits / 4 files" was the PR-own subset, not a tree-wide reading.

F3 — six numbers re-derived on both trees, and the twins agree again.

tree \.breadcrumb\b tree-wide -- packages/ \.breadcrumbs tree-wide
base 93127bd6f 12 files / 48 lines (rc 0) 10 files / 45 lines (rc 0) 0, rc 1
head 24a14c272 16 / 54 (rc 0) 13 / 50 (rc 0) 4 files / 6 lines (rc 0)
head minus the pin's 8 exclusions 11 / 46 (rc 0) 0, rc 1

comm -13 over the two file lists gives exactly four additions, every one this PR's own — the changeset, the refusal test, layout.ts, zod/layout.zod.ts — each matching only because it quotes the probe string; and comm -23 is EMPTY, so nothing at base left the set. That is what turns "the head number is inflated by our own files" from an argument into a reading. Controls: silent token rc 1 / 0 files on both trees; firing bare-word breadcrumbs 13 files at base, 17 at head. The frame is now stated in one sentence at all four sites, base numbers as the measurement and head numbers as this branch's echo — ⛔ no sentence mixes trees, and layout.ts and zod/layout.zod.ts agree again. ⛔ No assertion changed; the four pin files are 78/78 green.

F2 — my precedent list, corrected at source. objectui#5905: 8 Clause-② hits across body + 18 comments, including the ruling's "Clause-②: yes, correctly declared"; both changesets minor. objectui#4919: 0 hits; card closed 2026-08-24 while the earliest Clause-② spelling is 2026-08-31 — same shape, ⛔ not a literal precedent. objectui#5453: 0 hits, and its ACCEPT record 5450663069 reads verbatim "the narrowing is not consumer-visible". ⇒ The changeset now credits #5905 alone for the clause reading and demotes the other two to corroboration of the level. ⛔ The minor level does not move.

F5 — window re-derived off the real merge-base. merge-base 93127bd6f (unchanged); main had moved two commits past what the review measured. Window 7 commits / 36 files; ours 2 commits / 7 files; true intersection 2 fileslayout.ts and zod/layout.zod.ts — moved only by 24d1eddb9 (PR #8914, objectui#8310). ⚠️ The wrong instrument, shown as a contrast: a raw two-dot HEAD..origin/main reads 41 files — five more than the window — because it folds this PR's own edits back in. git merge-tree --write-tree rc 0, no conflict; self-comparison control byte-equal to HEAD^{tree}; and — ⭐ the part that makes rc 0 a measurement rather than a silent probe — a firing control: a synthetic commit off the merge-base rewriting the same line PR #8914 rewrote (mutation proven on disk by anchor count before reading any result) makes merge-tree return rc 1 with a real CONFLICT (content). Built through a scratch GIT_INDEX_FILE: ⛔ no ref moved, no index touched, ⛔ no merge taken.

Scope proof. Over both source files and the test file, changed lines that are not comment lines = 0 (grep rc 1), against a firing control of 35 total changed lines. Gates at 073026a4f: lint rc 0, build rc 0 (128 emitted files verified), type-check rc 0 with 0 error TS, the four pins 78/78, vitest run packages/types/ 164 files / 3209 tests rc 0, check:doc-fences rc 0 with the ledger identical (227 documents / 80 files / 89 blocks), all four changeset gates rc 0.

⚠️ NOT MEASURED, stated rather than counted green: full-farm lint, check:doc-snippets, check:doc-examples, check:skill-examples, docs:check-links — this round edits nothing under content/docs or skills, and check:doc-fences, the gate that reads that population, is green on an identical ledger. Declared to CI. CI convergence on 073026a4f was not waited on by the round.

⛔ Where this PR now stands

All five items are closed and the round moved no face. ⛔ It still does not land. The delta review that produced these findings is a SELF-REVIEW under contract-review.md — implementer and reviewer both trace to this session — and re-reviewing the new head from this same seat would only reproduce that. So needs:contract-review stays hung on this PR and on objectui#8871, the PR stays draft, ⛔ not enqueued. It is parked on the same escalation as PR #8865, which is the maintainer's or the director seat's to resolve.

One PM-owed item from the round: objectui#8871's card body still calls objectui#8310 "already open" — it closed at 03:11:41Z when PR #8914 merged. Card bodies are mine; fixing it now.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 10, 2026 04:20
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 10, 2026
Merged via the queue into main with commit 2028b31 Sep 10, 2026
35 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-8871-breadcrumbs-page-node branch September 10, 2026 04:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(types): breadcrumbs on the SDUI page node has no reader — the same class as objectui#7926, one key over, deliberately left parsing

3 participants