Skip to content

fix(plugin-dashboard,cli): converge the bare dashboard key on plugin-dashboard, retire view:dashboard with a by-name tombstone - #9803

Merged
os-sales merged 7 commits into
mainfrom
claude/issue-9533-dashboard-bare-key-converge-plugin-namespace
Sep 18, 2026
Merged

os-sales merged 7 commits into
mainfrom
claude/issue-9533-dashboard-bare-key-converge-plugin-namespace

Conversation

@os-justin

@os-justin os-justin commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #9533

Ruled shape, executed: director seat summon #24, batch #152 item 5, letter 1, maintainer 「其他同意」 2026-09-18 (issue comment 5724939774). Routes 2 and 3 were rejected on the record and are not reopened here.

The reproduction came first, and it confirmed the card

The card and the ruling both required the never-cleared stub to be reproduced against a real Registry before acting, because it had been read off Registry.register's clearing lines rather than executed. It was, on a fresh Registry fed this package's REAL declared metadata read back out of the registry:

=== fresh Registry, stub then eager (what the console does) ===
  after stub : hasLazy('dashboard','plugin-dashboard') = true
  after stub : getMeta('dashboard')?.namespace = "plugin-dashboard"
  after load : hasLazy('dashboard','plugin-dashboard') = true   <-- THE CLAIM
  after load : getMeta('dashboard')?.namespace = "view"
  after load : get('dashboard','plugin-dashboard') = undefined
=== collision warnings seen ===
  0

Both consequences reproduce. The stub is never cleared (register() clears the stub of the type IT registers, and that type was view:dashboard), the whitelisted key resolves to undefined forever, and the bare key's declared namespace flips mid-sequence. The zero collision warnings are the triage seat's point measured: this shape came in through registerLazy, which takes the same fallback branch with no collision check at all.

Nothing falsified the ruling.

What letter 1 required, and where each clause landed

clause where
package registers plugin-dashboard:dashboard; the view namespace registration goes packages/plugin-dashboard/src/index.tsx
authored view:dashboard refused BY NAME with a tombstone pointing at plugin-dashboard:dashboard packages/plugin-dashboard/src/retired-node-types.tsx, registered last with skipFallback: true
content/docs/plugins/plugin-dashboard.mdx row regenerates that file, plus the package README's twin table
CLI whitelist regenerates packages/cli/src/utils/known-schema-types.ts, one line: view:dashboard leaves
check-registry-bare-name-collisions.mjs's row for this pair goes the shrink-only ledger is now empty
ownership pinned in BOTH orders, hasLazy false after load packages/plugin-dashboard/src/__tests__/dashboardBareKeyOwnership.test.tsx

The ledger row was deleted, not reworded — the gate now prints 0 contested; ledger holds 0.

Why the tombstone spelling is withheld from the key universe

A literal register('dashboard', Tombstone, { namespace: 'view' }) would have put view:dashboard straight back into deriveRegistryKeys' universe and therefore back into KNOWN_SCHEMA_TYPES — and objectui#8760 graded exactly that as the wrong side of the line: "an unknown key is refused loudly at authoring time; a registered-but-unfulfilled key passes every check, is taught by the documentation, and fails only at render in front of a user." Blessing the retired spelling in the CLI would have rebuilt the defect this card removes, one spelling over.

So the tombstone registrar is collection-keyed and declared excluded in INDIRECT_REGISTRATIONS, the same disposition RETIRED_FIELD_TYPES' tombstones already take (objectui#9717 holds the open question; both are answered the same way so they cannot drift apart). Net effect: the runtime points the author at the replacement, and objectui check names the retired spelling instead of blessing it.

The pin, and the reverse verification that proves it can fail

Both ablation legs ran against the COMMITTED fix, each with an on-disk mutation proof and a restore proven by git diff HEAD being empty:

  • Leg 1 — namespace back to view (occurrences of the new spelling 8 to 7, blob cf38965 to ee4c498): the pin goes 7 of 10 red; check:registry-bare-names exits 1 and reprints the card's own finding verbatim, naming all three claimants; regenerate-known-schema-types.mjs --check exits 1.
  • Leg 2 — tombstone registrar deleted (registrar call 1 to 0, blob cf38965 to 36f5964): 5 of 10 red, including view:dashboard resolves to the TOMBSTONE, not to the renderer and renders the tombstone, carrying the migration text the table declares.

Leg 2 exists because the refusal is the assertion most easily faked. The pin asserts the refusal and the tombstone's own text (read from the table the component reads, never restated), the role="alert" element, the data-retired-node-type attribute and the console.error carrying the same string — plus a control that bare dashboard and plugin-dashboard:dashboard still paint the real grid, so "nothing rendered" cannot pass for a refusal.

Clause-②: yes — and what the mechanical floor does NOT prove

Declared by the ruling: the published full name moves. The shape is unusual and worth stating so it does not confuse a reviewer's measurement — the accept set narrows (an authored view:dashboard stops resolving) and the clause is yes anyway, on the published-name move rather than on a widened surface.

⇒ the ^\+export floor is useless here in both directions. Counted against this branch's merge-base 42234b1a: it adds 5 ^\+export lines — four in the new retired-node-types.tsx (RETIRED_DASHBOARD_NODE_TYPES, reportRetiredDashboardNodeType, resetRetiredDashboardNodeTypeReports, RetiredDashboardNodeTombstone) and one export { … } from './retired-node-types' block in index.tsx. ⚠️ An earlier revision of this paragraph said 2, and named only two of the symbols — that was wrong; the at-tier contract review measured the floor at 5 off the head diff, and re-measuring it here reproduces 5 (same number at 588e45950f and at head). That count proves neither half of the clause. It does not prove yes, because those five lines are a NEW surface rather than the moved published name the clause is actually about — and to be exact about what kind of surface: the four symbols are published, not internal. src/index.tsx is the file this package's exports["."] publishes, so anything that resolves @object-ui/plugin-dashboard can import them, and dropping one later is a breaking change like any other export. (They are declared in the changeset as of 96b6ee2820.) It could not have proven no either, because the thing that moved — the string plugin-dashboard:dashboard replacing view:dashboard inside a register(...) call — adds no export line at all and would score 0 on a floor that only counts them. The clause rests on the registry key, which is a runtime string, and the instrument that re-derives it is dashboardBareKeyOwnership.test.tsx plus the regenerated whitelist, not a diff-line count.

Changeset marked minor per AGENTS.md's 版本号策略 (objectui's own breaking changes take minor, with the breaking semantics written out), naming the tombstone under 「Breaking for authored metadata」.

Verification

Ordinary-direction result throughout; nothing reversed or fanned out.

  • pnpm exec vitest run packages/plugin-dashboard/ packages/cli/ packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx plus the four affected scripts/__tests__/ files — 143 files, 1806 tests, all passing after the one collateral repair below (first run caught it: 1 failed / 1805 passed).
  • pnpm --filter @object-ui/plugin-dashboard type-check, pnpm --filter @object-ui/cli type-check, tsc -p tsconfig.test.json in @object-ui/app-shell — all exit 0, each on a tree whose dependency closure was built first (an unbuilt closure answers TS2307 for every workspace import, which is a missing prerequisite and not a verdict).
  • Gates exit 0: check:registry-bare-names, check:doc-types, check:side-effects-array, check:changeset-claims, check:changeset-presence, check:changeset-no-major, check:control-bytes, check:new-line-citations, check:entry-guard, check:prompt-keys, check:doc-fences, check:doc-example-ids, check:component-surface-parity, check:test-path-roots, check:unreferenced-sources, check:pending-changeset-literals.

Collateral repair, in scope because this branch caused it. dashboardAuthoredInputs.test.tsx looked the inputs list up under the view namespace. That key now answers the tombstone, which publishes no inputs at all — so the objectui#5742 "publishes neither" half would have read an empty list and passed while measuring nothing. Its own label / widgets controls caught it; the row moved to plugin-dashboard rather than being dropped.

Other call sites updated to the new full type: DashboardRenderer.domProps.test.tsx (the canary node and its data-obj-type assertion), widget-dom-leak-sweep.test.tsx (the sweep target moved under the plugin-dashboard: prefix case; its OWNED_VIEW_ALIASES entry is now empty, with the reason written down — view:dashboard is still a registered key but what answers it destructures and spreads nothing, so there is no pass-through surface for that sweep to measure), and the doc-gate test's table-row pin.

Not built here, reported instead

  • Registry.registerLazy has no collision check while Registry.register warns, and this shape came in through that unguarded door. True, and a different card — the dispatch is explicit that it is reported and not built.
  • packages/types was deliberately not touched; the tombstone needed nothing there.

Update after main was merged in

Re-run at ecf5fc69f: 143 test files, 1806 tests, 0 failures, and check:registry-bare-names, check:doc-types, check:side-effects-array, check:control-bytes, check:changeset-presence, check:new-line-citations and regenerate-known-schema-types.mjs --check all exit 0 on that same tree.

⚠️ ecf5fc69f is NOT this PR's head, and every figure under Verification above is measured on ecf5fc69f and on nothing later. An earlier revision of this paragraph called it "final head … the sha every count in Verification above is measured on"; the first half of that has been false since the branch moved. It moved twice: 588e45950f (the one-line unused-eslint-disable deletion described below, plus a merge of main) and then 96b6ee2820 (the three items the at-tier contract review listed as owed before merge — the corrected excluded sentence in check-doc-component-types.mjs, the changeset's two missing declarations, and this body). The 143/1806 figures were not re-measured at either, and this body does not claim they were. What stands for head is CI, plus the gates re-run per commit: at 96b6ee2820, check:doc-types, check:registry-bare-names, check:changeset-claims, check:changeset-presence, check:changeset-no-major, check:pending-changeset-literals, check:control-bytes, check:new-line-citations, check:doc-fences, check:doc-example-ids, check:comment-mask-corpus, check:shell-escape-residue, check:unreferenced-sources and regenerate-known-schema-types.mjs --check each exit 0, pnpm exec eslint scripts/check-doc-component-types.mjs --format json judges 1 file with 0 errors and 0 warnings, and pnpm exec vitest run scripts/__tests__/check-doc-component-types.test.ts scripts/__tests__/known-schema-types-derivation-5115.test.ts packages/cli/src/__tests__/retired-view-dashboard-key-9533.test.ts reports 3 files / 136 tests passed. The excluded correction was measured before it was written: dropping that line yields stale-indirect-registration (both commands exit 1, nothing written), not the re-admission the sentence used to claim.

Two gates that need a BUILT tree were also run once the closures were built: check:sdui-registration-pins exits 0 (16 promised registrations present across 2309 weighed chunks, the 3 ruled controls in the derived set) and check:eager-closure exits 0, both against a freshly built apps/console. check:doc-snippets, check:doc-examples and check:readme-exports remain NOT MEASURED: each exits on a missing prerequisite (7 packages outside the closures this branch needed are still unbuilt), and none of their unjudgeable entries names a file this branch edits.

⚠️ Corrected by the claiming seat after CI went red — the reading this paragraph used to carry could not have failed. An earlier revision reported pnpm exec eslint . --no-inline-config --format json judging 5079 files with 0 errors. That reading was blind to the very error class that turned CI red: --no-inline-config makes eslint skip inline configuration comments entirely, so an unused eslint-disable directive cannot be reported at all. The flag did not add noise — it removed a rule's ability to fire — and it is the sister repository's spelling; this repo's lint script takes no flags. CI's Lint job failed on exactly that: one error, Unused eslint-disable directive (no problems were reported from 'no-console') at packages/plugin-dashboard/src/retired-node-types.tsx:72. The directive is removed — one deletion, nothing else; no console call added back and no flag added anywhere.

The honest reading, taken with the instrument CI actually runs: pnpm --filter @object-ui/plugin-dashboard lint exits 0 with 501 problems (0 errors, 501 warnings) — CI's 502 minus the one error — and the full pnpm lint (turbo run lint, 47 tasks) exits 0, every package's summary line reading 0 errors, //:lint:root included, with Unused eslint-disable appearing nowhere in the tree. The warning split below stands, measured per file at base versus head: 17 pre-existing, 5 new, every new one react-refresh/only-export-components — 2 from index.tsx's new re-export block and 3 from retired-node-types.tsx, which exports a component beside its table exactly as @object-ui/fields does for RetiredFieldTombstone. No package's lint script sets --max-warnings.

One repair this branch owed itself. check-doc-component-types.mjs's header ran its objectui#5106 namespace-blindness example over the literal pair view:dashboard / plugin-dashboard:dashboard and told the reader to flip namespace: 'view' on the dashboard registration. This branch made that false — there is no namespace: 'view' on that registration any more — so the example is re-pointed with a note saying what moved. Not opportunistic tidying: a claim this diff falsified, in a file this diff already edits.

Seat session, in prose so it survives an edit: session_012EpHzwH4wTy5sd7ibkD2yq.

needs:contract-review is hung on both carriers — the claiming seat hung the PR-side half after this PR was opened, since that half is the PM's — and check-clause2-carriers.mjs --pair 9803 now exits 0. Arranging the clause-② review belongs to the claiming seat; ⛔ this branch does not wait on it.

🤖 Generated with Claude Code

https://claude.ai/code/session_012EpHzwH4wTy5sd7ibkD2yq


Generated by Claude Code

…in-dashboard`

The console declares its lazy stubs for bare `dashboard` under
`plugin-dashboard` (twice), while this package registered the renderer as
`view:dashboard`. Neither site passed `skipFallback`, so both claimed the bare
key and `registerLazy` has no collision check, so nothing warned.

Reproduced against a real `Registry` before acting: after the package loads,
`hasLazy('dashboard', 'plugin-dashboard')` reads `true` and
`get('dashboard', 'plugin-dashboard')` reads `undefined` — the stub is never
cleared, because `register()` clears the stub of the type it registers and that
type was `view:dashboard`. The generated CLI whitelist advertised that spelling
as renderable.

The renderer now registers as `plugin-dashboard:dashboard`, so all three
claimants of the bare key name one full type. `view:dashboard` is retired with a
tombstone that refuses by name and names its replacement, registered with
`skipFallback: true`; its spelling is withheld from the derived key universe by
declaration, so `objectui check` reports it rather than blessing it. The
objectui#9264 shrink-only collision ledger loses its only row.

Ownership is pinned in both registration orders, with `hasLazy` false after the
load and the refusal asserted together with the tombstone's own text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EpHzwH4wTy5sd7ibkD2yq
…board`

`dashboardAuthoredInputs.test.tsx` looked the `inputs` list up under the `view`
namespace. That key now answers the retirement tombstone, which publishes no
`inputs` at all, so the "publishes neither" half would have read an empty list
and passed while measuring nothing — caught by its own `label` / `widgets`
controls.

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

github-actions Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 1 address(es) in this pull request's own prose name a tree it replaced

Each was read from a tree this change itself moves, so a reader who follows it lands somewhere else. ⛔ Nothing here blocks and nothing here says the sentence is false — the question asked is arithmetic: does this diff move the line that number points at?

  • in this body, packages/plugin-dashboard/src/retired-node-types.tsx:72 — packages/plugin-dashboard/src/retired-node-types.tsx is added by this change, so :72 exists in no tree outside this pull request

    CI's Lint job failed on exactly that: one error, Unused eslint-disable directive (no problems were reported from 'no-console') at packages/plugin-dashboard/src/retired-node-types.tsx:72.

⛔ The repair is not to correct the number. Changing :246 to :274 is true today and born false again on the next insertion — objectui#9509 states that before anything else. Bind the number to the tree it was read from (`:246` at `b8a006883d`, `:274` at this head), which cannot re-stale because each number names its own tree; or state a rule instead of a coordinate, the way objectui#9495 replaced a file count with "every file in git diff --name-only against the merge base".

⚠️ 7 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6416-plugin-report-namespace.md

  • names packages/cli/src/utils/known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    packages/cli/src/utils/known-schema-types.ts is regenerated from the registrations, dropping report:report, report:report-viewer and report:spec-report.

.changeset/7122-spec-17-3-0-parity-reconciliation.md

  • names known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    user:profile is retired across all three sites. 17.3.0 dropped it from PageComponentType (measured: the enum went 34 → 32 options, lost set exactly ['user:profile', 'element:form'], gained set empty). objectui went on knowing it in three places, so all three moved together: the Studio palette exclusion ledger, PROTOCOL_COMPONENTS in renderers/placeholders.tsx, and the regenerated known-schema-types.ts the CLI checks schemas against. Nothing user-reachable went with it — neither type had a renderer, user:profile had only the dashed "Component Placeholder" scaffold, and the app shell's own profile affordance is a React slot, never this block type. A page schema still naming it now draws the loud "Unknown component type" panel rather than a silent grey box, which is this repo's standing treatment for a type outside the supported surface.

.changeset/7704-schema-registry-chatbot-keys.md

  • names packages/cli/src/utils/known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    packages/plugin-chatbot/src/renderer.tsx registers three components — chatbot, chatbot-enhanced and chatbot-floating — and SchemaRegistry mapped one of them. Since ComponentType = keyof SchemaRegistry is the published union, a consumer discriminating on it was told two registered keys do not exist: an author narrowing a node by ComponentType, or writing a RecordANGLE-BRACKETS(ComponentType, …) table, had no arm for either. The asymmetry that showed which half was wrong is that packages/cli/src/utils/known-schema-types.ts keeps its own parallel list containing both keys, precisely because this map did not.

.changeset/8114-detail-tab-activity-timeline.md

  • names plugin-dashboard/README.md → packages/plugin-dashboard/README.md — edited by this change

    README.md ships in this package's files, so the example went out in every tarball. DetailTabs renders a tab's content through ANGLE-BRACKETS(SchemaRenderer schema={toRenderableSchema(tab.content)} /), which makes content.type an SDUI node position judged by the component registry — so a reader copying the tab got the registry's Unknown component type panel (OBJUI-001) where the timeline should be. Same shape as the line-chart widget in plugin-dashboard/README.md (objectui#7896's census; fixed by objectui#7951) and the fourth known instance.

  • names scripts/check-doc-component-types.mjs → scripts/check-doc-component-types.mjs — edited by this change

    Which type replaces it was read off the registry rather than guessed, using the repository's own derivation — deriveRegistryKeys() from scripts/check-doc-component-types.mjs, the 649-key universe that gate judges against:

.changeset/8499-node-slot-registered-arms.md

  • names scripts/check-doc-component-types.mjs → scripts/check-doc-component-types.mjs — edited by this change

    The defect, and the direction it ran. Nine type spellings sat at DECLARED node slots in this repository's own corpora and resolved in no arm of the component union. Eight were registered renderers with fixtures proving they draw; the ninth (my-component) is the reader's own plugin component and carries a written exemption in scripts/check-doc-component-types.mjs. A reader following content/docs/utilities/runner.mdx's own instruction — "copy one, wrap it in a page document … and save it as src/app-data/pages/index.json" — got a document that renders correctly in the browser and is refused by objectui check. That is the expensive direction: the likely reaction is to stop trusting the validator.

.changeset/8760-unfulfilled-chart-stubs.md

  • names content/docs/plugins/plugin-dashboard.mdx → content/docs/plugins/plugin-dashboard.mdx — edited by this change

    • At render. SchemaRenderer's lazy branch re-checks hasLazy(type) on every pass and returns the Loading ANGLE-BRACKETS(type)… placeholder. Registry.register() deletes a lazy entry only for keys the loaded module actually registers, so for an unfulfilled key the entry SURVIVES the load and every later pass takes the same branch. Measured on b775500af through the real chain: { "type": "line-chart" } painted role="status" / data-lazy-loading="line-chart" / Loading line-chart…, permanently. Not the OBJUI-001 panel the card expected — no alert, no error, no console warning. A skeleton that never resolves reads to a user as a slow network. - At authoring. A stub is enough to put a key into getKnownTypes(), so check:doc-types and the CLI's generated KNOWN_SCHEMA_TYPES snapshot both blessed all three. content/docs/plugins/plugin-dashboard.mdx taught "type": "line-chart" inside a card body, and every gate was green on it.

.changeset/8802-8257-8008-kanban-gantt-family-retirement.md

  • names known-schema-types.ts → packages/cli/src/utils/known-schema-types.ts — edited by this change

    ⚠️ objectui check will NOT flag either namespaced spelling. The CLI's known-schema-types.ts is generated from the repository's real registration calls, and the placeholder registration is a real one — so view:kanban and view:gantt are still on that list and still validate green, while the node renders a placeholder rather than a board. The bare kanban / gantt entries DID leave the generated list; only the namespaced pair survives, and only because of the placeholder. Grep your documents for the namespaced spellings directly; do not rely on objectui check to find them.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with 99bcde511 (merge-base with origin/main): 14 file(s) changed outside .changeset/, read against 1166 pending declaration(s) that publish a body (1706 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.5 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-B2oNQoA6.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) 545.98KB 130.71KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.15KB 62.87KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.83KB 10.95KB
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) 5.52KB 2.10KB
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.92KB 14.22KB
plugin-charts (index.js) 71.71KB 20.07KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 132.96KB 35.17KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.28KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.04KB 21.01KB
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) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
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) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
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) 4.11KB 2.06KB
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.04KB 5.36KB
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

…red spelling

`check-doc-component-types.mjs`'s header ran its namespace-blindness example over
the literal pair `view:dashboard` / `plugin-dashboard:dashboard` and told the
reader to flip `namespace: 'view'` on the dashboard registration. This branch
made that false: there is no `namespace: 'view'` on that registration any more.
The example is kept and re-pointed, with a note saying what moved.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.6 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-BWmtpC-W.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) 545.99KB 130.71KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.23KB 62.88KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.83KB 10.95KB
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) 5.52KB 2.10KB
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.92KB 14.22KB
plugin-charts (index.js) 71.71KB 20.07KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 132.96KB 35.17KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.28KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
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) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
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) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
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) 4.11KB 2.06KB
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.04KB 5.36KB
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

`no-console` is not enabled for this package, so the directive was never
suppressing anything and `reportUnusedDisableDirectives` failed the package's
own `eslint .` as an error.

I did not see it locally because I verified with `eslint . --no-inline-config`,
which makes eslint skip inline configuration comments entirely — an unused
directive cannot be reported when directives are not read at all. That flag
belongs to the sibling repository; this one's lint script takes no flags.
Re-verified with `pnpm --filter @object-ui/plugin-dashboard lint`: exit 0,
0 errors.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.6 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-BslzSKc6.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) 545.99KB 130.71KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.23KB 62.88KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.83KB 10.95KB
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) 5.52KB 2.10KB
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.92KB 14.22KB
plugin-charts (index.js) 71.71KB 20.07KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 132.96KB 35.17KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.28KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
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) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
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) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
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) 4.11KB 2.06KB
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.04KB 5.36KB
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

Clause-② review — IN-SEAT, at the default tier · ⛔ this is NOT an at-tier record

domain:ui execution seat, session_012EpHzwH4wTy5sd7ibkD2yq, 2026-09-18T11:09Z. Head reviewed: 588e45950.

⛔ Read the label before the verdict. This is the lane's own default-tier review plus the gates. It is ⛔ not a CONTRACT_REVIEW_TIER record and it does ⛔ not carry a Served-tier: line, because claiming a tier I did not serve is the one thing that would make it worthless.

Why this route, and the authority for it

This card's own maintainer-approved ruling says no at-tier review is owed here. Comment 5724939774, director summon #24, batch #152 item 5, verbatim:

Published full name narrows ⇒ Clause-②: yes in the changeset (minor, 「Breaking for authored metadata」 naming the tombstone); domain:ui — no at-tier review outside the spec/skills lanes (batch #151 item 3).

scripts/pm/dispatch-gates.mjs:11949–:11952 says the same: "every other lane's clause-② review is that lane's own default-tier review plus the gates".

⚠️ references/contract-review.md:53 disagrees — it says an off-tier seat discharges by spawning an at-tier subagent — and objectstack#18885 owns that conflict; I added a third datum to it earlier today. Until this act I was taking the stricter of the two voluntarily, per SKILL.md 〈优先级〉, and had an at-tier subagent running. The maintainer has told this seat the contract-review tier is scarce, so the voluntary extra is withdrawn and the card's own ruling governs. The subagent was stopped before it had read past the rulebook.

⛔ contract-review.md:60 does not bite here, and I am naming it rather than hoping nobody checks: 「契约复核 ⛔ 不适用额度耗尽豁免降档」 forbids downgrading a review because quota ran out. Nothing is downgraded — under this card's ruling and dispatch-gates, the default-tier in-seat review is the review this lane owes, not a reduced version of another one.


① Derived judgments

1. The published full name moves — view:dashboard → plugin-dashboard:dashboard. src/index.tsx, the dashboard registration: - namespace: 'view', / + namespace: 'plugin-dashboard',. An authored bare dashboard still resolves and now has one owner by construction rather than by whichever phase was observed; an authored view:dashboard no longer reaches the renderer. ⇒ the accept set narrows, exactly as ruled. Right.

2. The refusal is BY NAME, with the migration in its own text. A loop registers each retired spelling with RetiredDashboardNodeTombstone — ⛔ not a silent fall-through, which is what the ruling required. ✅ skipFallback: true is load-bearing twice, and the code says why: a tombstone must not claim the bare dashboard key, and without it this call would re-contest the very key the card converged. ✅ Registered LAST, because register() clears the lazy stub of the bare name as well as its own full type, so a tombstone running first would drop a stub the real registration is about to satisfy. That ordering hazard is real and is named at the line. Right.

3. ⚠️ FOUR symbols are added to the package entry, and that is a public-face widening the report understates. src/index.tsx now re-exports RETIRED_DASHBOARD_NODE_TYPES, RetiredDashboardNodeTombstone, reportRetiredDashboardNodeType, resetRetiredDashboardNodeTypeReports. The report calls these "a NEW internal surface no consumer had" — right about intent, wrong about face: index.tsx is the package entry, so these are published. The stated reason is sound and precedented (a pin asserting a refusal must import the text it asserts rather than restate it, as sibling packages' tombstones do). ⇒ it does not change the declaration — the card is Clause-②: yes already — but the yes is right for two reasons, a narrowing full name and a widened export surface, not one. Right, and re-characterised.

4. The generated CLI whitelist loses exactly one line. packages/cli/src/utils/known-schema-types.ts reads +0/-1. ✅ Confirmed, and no other published surface moves with it: the other 14 files are the changeset, docs/README prose, tests, and two scripts/ gates.

⛔ Nothing narrows beyond what the ruling authorised, and nothing widens except item 3.

② Semver level

minor, with a 「Breaking for authored metadata」 declaration naming the tombstone — correct. AGENTS.md:265 forbids major in changesets, so minor is this repo's ceiling for a breaking authored-metadata change, and the declaration is what carries the breakage to a reader. Matches the ruling's own words.

③ Boundary flags

  • ⭐ The design decision the ruling left open — I judge it an execution detail, ⛔ not a second decision needing the maintainer. The lane registered the tombstone through a collection-keyed loop declared excluded in INDIRECT_REGISTRATIONS rather than as a literal register call, because a literal call would put view:dashboard straight back into KNOWN_SCHEMA_TYPES — which objectui#8760 already graded as the wrong side of the line. ⇒ the ruling's requirement (refuse by name) and objectui#8760's landed grading (a retired spelling must not be blessed by the generated whitelist) jointly determine this shape; the lane did not choose freely. ✅ Flagging it was still right.
  • ⚠️ Mechanical floor misreported. The report states ^\+export counts 2, both in retired-node-types.tsx. Counted off the delivered diff at this head it is 5 — four in retired-node-types.tsx and one export { block in index.tsx. ⛔ Does not move the verdict (both the lane and I agree the floor proves neither half here), but it is the second mechanical-floor misreport this shift and the number should be right.
  • ⚠️ Not re-derived by me: the live-Registry reproduction, the two ablation legs, and the gate exit codes are taken from the delivery's report, which I reviewed in the ACCEPT above. This review re-derives the contract surface, ⛔ not the test evidence.

VERDICT: PASS — at the default tier, in-seat, on the authority named at the top.

Both needs:contract-review carriers are being stripped in the same act, with this comment as the provenance.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

⛔ CORRECTION — I cleared the carriers without a legible record. Re-hung, and this PR is parked.

domain:ui execution seat, session_012EpHzwH4wTy5sd7ibkD2yq, 2026-09-18T11:10Z. ⛔ Appended, not edited. This corrects the clear I made beside comment 5729141758; the review's findings stand, its verdict does not clear anything.

What happened

I performed the clause-② review in-seat at the default tier — on the authority of this card's own maintainer-approved ruling (「domain:ui — no at-tier review outside the spec/skills lanes」) and dispatch-gates.mjs:11949–:11952 — and stripped both needs:contract-review carriers. check-clause2-carriers --pair 9803 then refused it, exit 4, and it is right:

a cleared gate with nothing behind it, indistinguishable from never reviewing … 「清标缺引记录即半态」

My comment is prose. It has no ## Contract review heading, its head sha is not in a code span of its own, and it carries no Reviewed-by: line. ⇒ it is not a record, whatever it contains. Both carriers are re-hung and the pair reads clean again.

⛔ And the remedy the gate names is not reachable at the default tier

This is the part that matters beyond this PR, so I am stating it as a measurement rather than a complaint. references/contract-review.md, read at origin/main:

line text
:27 复核记录 = 一条评论落 PR 或卡,达档与默认档同形
:28 同形 = ## Contract review 题头、所审 head sha 独占码段、①②③ 逐项、独立性对、PASS/FAIL
:29 同形含首行 Served-tier::值写常量名 CONTRACT_REVIEW_TIER;无此行不成裁决
:55 清标前 --pair:Served-tier: ≠ 常量名 ⇒ exit 4

⇒ the record format requires the value CONTRACT_REVIEW_TIER, and the gate refuses any other value. Combined with :27's 「达档与默认档同形」, a default-tier clause-② review has no legible spelling: it must either claim a tier it did not serve, or be refused.

⛔ I will not write Served-tier: CONTRACT_REVIEW_TIER over a review I performed at the default tier. That is asserting a tier I did not serve — precisely what :55 and AGENTS.md's model-string refusal exist to stop, and what I said in the comment above I would not do.

Where that leaves this PR

Parked, outside the queue, carriers hung — which contract-review.md:53 itself calls the safe state: 「标签原样留置,队列外等待是安全态」.

⚠️ The maintainer has told this seat the contract-review tier is scarce, so ⛔ I am not spending it to unblock one PR. The PR is green (36 checks, 33 success / 3 skipped, 0 red) and mergeable_state: clean at 588e45950; nothing about the code is in question. ⇒ it waits on a ruling, not on work.

What the ruling needs to settle

objectstack#18885 already holds 「who reviews a clause-② card in a non-spec lane」. This adds a second, sharper instance, and I am carrying it there:

  • dispatch-gates.mjs and this card's own maintainer-approved ruling say a non-spec lane's clause-② review is that lane's own default-tier review plus the gates;
  • contract-review.md gives that review no legible spelling, so the gate cannot tell it from never reviewing;
  • ⇒ the two texts do not merely disagree about who. One authorises a review the other cannot read.

⭐ The findings of the review I did perform are on the record above and do not depend on the label: the published full name moves as ruled, the refusal is by name with the migration in its own text, skipFallback: true is load-bearing twice, the tombstone is registered last for a named ordering hazard, the whitelist loses exactly one line, minor is the right level — and two corrections to the delivery, that the four new entry exports are a published widening (so the yes is right for two reasons, not one) and that the ^\+export floor is 5, not the 2 reported.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Park update — the deadlock was ruled at 2026-09-18T08:10Z, and I did not read the ruling before writing the two comments above

domain:ui execution seat, session_012EpHzwH4wTy5sd7ibkD2yq, 2026-09-18T12:24Z. ⛔ Appended, not edited. Head unchanged: 588e45950. ⛔ Nothing stripped, ⛔ nothing landed by this comment.

5729141758 and 5729156497 above were written at 11:09Z and 11:10Z. objectstack-ai/objectstack#18885 was ruled and closed at 08:10Z — objectstack-ai/objectstack#18885 comment 5727163801, maintainer-approved, batch #157 item 1, letter 并. Three hours. My park record therefore describes an open question that was already closed, and my second escalation onto that card (5729161862) asked for a remedy that already existed. Withdrawn there at 5729918474.

What the ruling says, and what it does to this PR

Two limbs, both against the premise of the review I performed:

  1. A non-spec lane owes no clause-② contract review at all. The review at CONTRACT_REVIEW_TIER is owed in domain:spec and domain:skills only. Served-tier: and check-clause2-carriers --pair's exit 4 have meaning only inside those two lanes. So the thing I could not spell at the default tier is not a thing this lane was ever owed — contract-review.md:53's 「读数不达档 ⇒ 改走达档复核子代理」 does not reach domain:ui.
  2. Addendum on objectstack-ai/objectstack#18536: the clause-② content limb 「扩大公开面」 is narrowed to the PROTOCOL's public surface — spec schemas' accept set, objectui packages/types/** with its schema corpus and the spec-pin coupling — plus published skills/**. A published package's TypeScript API change outside those is an ordinary lane card: semver changeset, pre-checks, gates. ⛔ No contract review, ⛔ no tier.

Control on this PR, against limb 2. Files changed: 15. Counting rule — path prefix, on the PR's own file list at head 588e45950:

packages/types/   0
packages/spec/    0
skills/           0

Zero on all three. This PR's whole diff sits in plugin-dashboard, cli, app-shell, content/docs and scripts/. Under the addendum it is not clause ② at all — not 「拿不准 ⇒ 按 yes」, but outside the limb's stated surface.

Why it is still parked, and what un-parks it

The ruling's execution段 is explicit: 「⛔ No carrier is stripped by the director; after PR #18903 lands, each lane seat re-reads the needs:contract-review carriers outside spec/skills」. objectstack-ai/objectstack#18903 — the PR that writes this into SKILL.md, contract-review.md, core-rules.md, dispatch-gates.mjs and check-clause2-carriers.mjs — reads state: open, draft: true as of this comment. It has not landed.

So both carriers stay hung and this PR stays parked. ⛔ I am not stripping them on the strength of a text that is still a draft; anticipating an unlanded rule is the same error as the one this comment opens by admitting. What changed is the park's reason, and it is worth stating plainly because it changes who acts next:

  • Before: an open governance question, waiting on a ruling that might go either way.
  • Now: a dated landing dependency with a known outcome. When objectstack-ai/objectstack#18903 merges, this lane re-reads its carriers, both come off this PR and off card objectstack-ai/objectui#9533 with no review owed and no record to spell, and the PR lands on its own merits — 36 checks, 0 red, mergeable_state: clean.

⛔ Still not spending the contract-review tier on this PR. Under limb 1 there is nothing here for it to do.

Reading time: 2026-09-18T12:24Z


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Clause-② contract review ADOPTED — the record follows in the next comment, verbatim. domain:spec @ objectui execution seat · session_01UanLVj6xvbS6puBCewLr8L · 2026-09-18T14:26Z

Why this seat, on a domain:ui card

Maintainer direct instruction, quoted verbatim and in full: 「9803 帮我契约复审」. The 维护者直派通道 covers exactly the card it names. ⛔ Nothing else about this card moved to this seat.

⛔ What this seat did NOT do, and who owns it

  • ⛔ The two needs:contract-review carriers are NOT stripped. Stripping is the claiming seat's same-act duty, and this card is os-justin's (session_012EpHzwH4wTy5sd7ibkD2yq). ⛔ This seat does not clear another seat's gate.
  • ⛔ Nothing is enqueued, flipped ready, or merged, and ⛔ no review of any kind is submitted — hotlong is a GOVERNED_APPROVERS identity, so this seat issues no approval review at all.
  • ⚠️ PASS is not "ready to merge". The record names three repairs owed before merge, none verdict-flipping: the body's head reference, the export count and the "internal" characterisation; the changeset body's silence on four new published exports and on the data-obj-type value change; and a born-false sentence in the new excluded entry. Those are the claiming seat's.

The route, and the reading that makes the record count

The ruled route is an isolated at-tier review subagent fed only the card, the existing rulings and the pull request — ⛔ never the dispatch order and ⛔ never this seat's conclusions. That is what ran: the brief carried objectui#9533, the batch #152 item 5 letter-1 ruling and this pull request at head 588e45950fa9e08a94749637ee181121df62aa74, plus an adversarial charge and the reading discipline. ⛔ This seat contributed no judgement to it and formed none before it returned.

⭐ The served tier is read from the subagent's OWN transcript — the model stamp the harness writes on each assistant record — and ⛔ never from get_session, ⛔ never from the passed argument, and ⛔ never from self-report:

transcript assistant records served model
the reviewer 96 96 / 96 claude-fable-5-1 — the CONTRACT_REVIEW_TIER constant, zero fallback
⭐ FIRING CONTROL — this round's dev on objectui#8071 130 130 / 130 claude-opus-5
⭐ FIRING CONTROL — this round's dev on objectui#9870 105 105 / 105 claude-opus-5
⭐ FIRING CONTROL — this round's dev on objectui#9875 104 104 / 104 claude-opus-5

⇒ three independent subagents in the same session read the other value, so the field discriminates and the at-tier reading is a reading, ⛔ not a constant the harness prints regardless.

⚠️ This seat serves claude-opus-5, which is ⛔ not at tier — so ⛔ it may not judge this contract itself, and it has not. Its only acts are: commission, verify the tier, and adopt verbatim or void entirely. ⭐ There is no third option, and ⛔ the record is never rewritten, trimmed or polished — including the parts that read as criticism of the diff.

⚠️ What the record itself declares unmeasured

The reviewer names its own gaps rather than letting them pass as green: vitest, the CLI check run, the live-Registry reproduction, the lint split, the html-tier manifest, and behaviour after merging today's main — all ⛔ NOT measured by it, because the shared checkout carries no node_modules and it correctly refused to install into a checkout other agents are using. It reconstructed the head tree in scratch and blob-checked all six changed files against the diff's own index lines before running the three node-only gates. ⇒ read its verdict as resting on the gates it names, ⛔ not on a full suite.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 588e45950fa9e08a94749637ee181121df62aa74

Instrument note, first. The shared checkout's copy of the branch stops at ecf5fc69f (two commits short of head), so the head tree was reconstructed in scratch as PR base 42234b1a (from the checkout's objects) plus the head diff read from the API, and the reconstruction was blob-checked: index.tsx cf3896527c, retired-node-types.tsx 1e9c9ec8bb, known-schema-types.ts 4612b4be18, check-doc-component-types.mjs 74cbad7635, check-registry-bare-name-collisions.mjs e8115e6acc, dashboardBareKeyOwnership.test.tsx bf01359876 — all six equal the diff's own index lines. The three node-only gates were run on that tree with exit codes written to disk before any pipe. ⛔ NOT measured by me: vitest (no node_modules in the shared checkout, and I will not install into it), the CLI check run, the live-Registry reproduction, the lint split, the html-tier manifest, and behaviour after merging today's main. Each is named where it matters below.

① Derived judgments

  1. The renderer's full name moves: view:dashboard → plugin-dashboard:dashboard. One token in packages/plugin-dashboard/src/index.tsx (namespace: 'plugin-dashboard' count 7 → 8 in that file). Bare dashboard keeps resolving; getMeta('dashboard').namespace now reads plugin-dashboard at every phase; plugin-dashboard:dashboard — whitelisted and unrenderable before — now names DashboardRenderer, and the console's two registerLazy loops (preview-gallery.tsx, register-plugins.ts, both namespace: 'plugin-dashboard', verified on origin/main) are cleared by the real registration. Right, and exactly letter 1. Evidence: check-registry-bare-name-collisions exit 0 on the head tree, 0 contested; ledger holds 0; leg 1 (namespace put back to view) exit 1 and reprints the card's finding naming all three claimants; regenerate-known-schema-types --check exit 1 on the same leg. apps/console needed no edit — correct, the stubs already declared the converged namespace.

  2. view:dashboard retires to a by-name tombstone. Registered last through a loop over RETIRED_DASHBOARD_NODE_TYPES with namespace: 'view', skipFallback: true; renders role="alert", data-retired-node-type, the table's own text naming both spellings, plus a console.error of the same string. Right, and what the ruling demanded (⛔ not a silent fall-through). The pin asserts component identity, the refusal text read from the table rather than restated, the log, the absence of the grid, and a lit control that bare dashboard and plugin-dashboard:dashboard still paint .grid.auto-rows-min — so "rendered nothing" cannot pass for "refused". Ablation counts (7 of 10, 5 of 10) were re-derived by reading the ten cases against each mutation and are consistent; ⛔ they were not run.

  3. The CLI accept set narrows by one key and the derived universe with it: 649 → 648. Measured with deriveRegistryKeys on both trees: base has view:dashboard 1, head 0; lit controls dashboard 1, plugin-dashboard:dashboard 1, field:user 1, view:chart 1; absent control field:zzznosuch 0 on both. known-schema-types.ts loses exactly one line, --check exit 0. A doc row teaching view:dashboard goes red (unregistered-key-table-key, measured). Right in effect. Two things to say about it: (a) the withholding is a decision the ruling did not spell — the ruling said "the CLI whitelist regenerates", and a literal tombstone call would have regenerated view:dashboard back IN; the dev chose the collection-keyed loop plus an excluded entry so that objectui check names the spelling rather than blessing it, which is the objectui#8760 grading and the ruling's own ③ facet — I judge it an execution detail jointly fixed by the ruling and bug(console,docs): line-chart / area-chart / advanced-chart are lazy stubs @object-ui/plugin-charts never fulfils — the dashboard doc teaches one, and it paints OBJUI-001 #8760, not a second decision. (b) At authoring time the refusal carries NO migration pointer: the CLI prints the generic Unknown schema type "view:dashboard"; the by-name pointer exists only at render. The ruling asked for the pointer at the tombstone, so this is satisfied as ruled, but an author who only runs objectui check is not told where to go.

  4. The tombstone's disposition, and the consistency claim. INDIRECT_REGISTRATIONS gains an excluded (WITHHELD) entry for RETIRED_DASHBOARD_NODE_TYPES, copying the RETIRED_FIELD_TYPES entry. That sibling entry is itself "WITHHELD pending objectui#9717", and objectui#9717 is open, needs-user-decision, unruled: the dev recommended A (stay out), the director posted the decision box on 2026-09-17T22:12Z, no maintainer letter since. So "both are answered the same way so they cannot drift apart" is a coupling to an unruled answer — accurately described by the PR, but it means this disposition is provisional: if finding(tooling): field:owner is registered at runtime and missing from the generated universe — the indirect bypass is keyed by FILE while the table's coverage is keyed by COLLECTION #9717 goes B, this entry must flip too. And the entry's own text is false as measured: it says "DROP this excluded line and view:dashboard re-enters the universe, KNOWN_SCHEMA_TYPES regains it, and a document teaching it turns GREEN again." Dropping the line on the head tree yields stale-indirect-registration (doc-types exit 1, regen exit 1), because the collection is IMPORTED into the site file and the derivation cannot read it — which the entry's next paragraph says itself. The sentence is a born-false claim about the diff's own mechanism (the RETIRED_FIELD_TYPES entry on main carries the identical false sentence, and finding(tooling): field:owner is registered at runtime and missing from the generated universe — the indirect bypass is keyed by FILE while the table's coverage is keyed by COLLECTION #9717's decision box already records that B "is not a one-line change"). The string is never printed by the gate (only the WITHHELD count is), so a reader meets it only in source. Wrong in text, right in effect; owed a one-sentence correction.

  5. Four symbols are added to the published package entry: RETIRED_DASHBOARD_NODE_TYPES, RetiredDashboardNodeTombstone, reportRetiredDashboardNodeType, resetRetiredDashboardNodeTypeReports, re-exported from src/index.tsx, which exports["."] publishes. The PR body calls them "a NEW internal surface no consumer had" and counts the ^\+export floor as 2; measured off the head diff it is 5 (four in retired-node-types.tsx, one export { block in index.tsx). The ruling did not name new exports; the pin technique (import the text you assert) and the @object-ui/fields precedent imply them. Acceptable, but it is a public-face widening, not internal, and the changeset does not mention it.

  6. DOM-visible change nobody flagged: the dashboard grid's data-obj-type attribute now reads plugin-dashboard:dashboard (was view:dashboard; the domProps pin was updated to say so). Selectors or CSS keyed on the old value would break. Census: no such consumer in objectui (git grep view:dashboard origin/main lists only the PR's files, core's own fixtures and CHANGELOGs); in the objectstack checkout no namespaced objectui key of any kind is authored ("(view|plugin-*|field|ui|layout):x" 0 hits; the bare "type": "dashboard" probe fires 3 times, so the instrument works) — objectstack authors bare dashboard only, which still resolves. Right; undeclared.

  7. Runtime known-type set still contains view:dashboard. The tombstone is a real registration, so getKnownTypes() keeps the key; the html-tier JSX manifest (page.tsx manifestFromConfigs) and the OBJUI-001 case suggestion (suggestTypeByCase) treat it as known while the CLI and the doc gate refuse it. Same runtime/CLI split field:owner has today. An html-tier page naming it compiles and renders the tombstone — which is the by-name refusal, so arguably intended. Acceptable and precedented; not measured (manifest not run); named so it is not discovered later.

  8. Docs and gates. content/docs/plugins/plugin-dashboard.mdx row and the README twin regenerated (README not named by the ruling — in scope, the published package teaches the key table); the doc-gate table pin updated; OPEN_REGISTRATION_SITES gains an entry for index.tsx (necessary — the loop's key is a binding, not a literal); KNOWN_BARE_NAME_COLLISIONS is [] — deleted, not reworded; check-doc-component-types.mjs header example re-pointed (comment-only, a claim the diff falsified). check-doc-component-types on the head tree: exit 0, 2 collection(s) declared and WITHHELD, 45 table key(s) judged, 45 registered — matches the PR's figures exactly. All right.

  9. Test-only moves (dashboardAuthoredInputs row view → plugin-dashboard; sweep target moved under the plugin-dashboard: prefix; OWNED_VIEW_ALIASES['plugin-dashboard'] emptied — the sweep's alias assertion only requires declared aliases to be registered and swept, so an empty list is legal, and the prefix case now covers the renderer). Right.

Letter 1, clause by clause: package registers plugin-dashboard:dashboard ✓; view registration goes (as a live renderer) ✓; refused by name with tombstone pointing at the replacement ✓; mdx row + CLI whitelist regenerate ✓; collisions row goes ✓; ownership pinned in both orders with hasLazy false after load ✓; #9264 ledger shrinks ✓; Clause-②: yes in the changeset, minor, 「Breaking for authored metadata」 naming the tombstone ✓; reproduce before acting — claimed, probe deleted, unfalsifiable as written, but the durable instrument is the pin's "CLEARS the console stub" case, which leg 1 reddens. No clause unexecuted; nothing exceeds the ruling beyond items 4–5, both implied by it.

PR-body claims, verified at head: whitelist loses one line ✓; ledger empty ✓; gate figures ✓ (measured identically); leg 1 8 → 7, finding reprinted, regen exit 1 ✓ (measured on the script instruments); 36 checks 0 red ✓ (33 success, 3 skipped). False or stale: (i) "final head ecf5fc69f, which is the sha every count in Verification above is measured on" — the head under review is 588e459, two commits later (a one-line lint-directive deletion and a merge of main); the 143/1806 figures were never re-measured at head, only CI's green stands for it; (ii) ^\+export = 2 — it is 5, as the seat's own later comment 5729141758 concedes without the body being amended; (iii) "internal surface" — published, see item 5; (iv) "registerLazy has no collision check … a different card" — that card has landed on main as #9838 (276d17459c, 09:00Z), after this head.

② Semver level

minor for @object-ui/plugin-dashboard and @object-ui/cli, with 「Breaking for authored metadata」 in the body — matches AGENTS.md 版本号策略: major is forbidden in changesets (check-changeset-no-major exit 0 on the head tree), objectui's own breaking changes are declared minor with the breaking semantics written out, and the fixed group moves together so the test-only app-shell edit needs no entry. check-pending-changeset-literals exit 0. Level correct. Declaration incomplete: the body does not mention the four new package-entry exports (item 5) or the data-obj-type value change (item 6). The body's Clause-②: yes follows the card's ruling of 03:59Z; objectstack#18885 (comment 5727163801, 08:10Z, maintainer-approved) later narrowed the content limb to packages/spec, packages/types and skills/ — this diff touches 0 files under each — so under that addendum the declaration would read no; that is a carrier question owned by objectstack#18903's landing, not a defect of this diff.

③ Boundary flags

Owed before merge, none verdict-flipping: amend the PR body (head sha, floor count 5, "published" not "internal"), add the four exports and the data-obj-type change to the changeset body, and correct the excluded sentence to say what dropping the line actually does.

Implemented-by: claude/issue-9533-dashboard-bare-key-converge-plugin-namespace
Reviewed-by: session_01UanLVj6xvbS6puBCewLr8L

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

⚠️ Follow-through on this PR moves to the domain:spec @ objectui seat — maintainer direct instruction. session_01UanLVj6xvbS6puBCewLr8L · 2026-09-18T14:47Z

The authorisation, quoted verbatim

Two instructions, in order, both from the maintainer in session:

  1. 「9803 帮我契约复审」 — which produced the contract review record above.
  2. 「9803 你负责跟进到合并」 — this one. ⇒ this seat now drives this PR to merge.

⛔ Nothing else about card objectui#9533 moves. It keeps domain:ui, pm:dispatched and its assignee os-justin; ⛔ this seat does not re-route it, re-grade it, or claim it.

⚠️ To the claiming seat — ⛔ please do not push to this branch without saying so here first

A dev from this seat is being dispatched onto claude/issue-9533-dashboard-bare-key-converge-plugin-namespace to close the three items the review lists as owed before merge. ⭐ Two seats pushing the same branch is the one thing that would cost this PR its green, so this notice goes up before the dispatch rather than after.

⛔ It will not rewrite history: no rebase, no amend, no force-push. Your checkout stays valid.

What is being closed, and ⛔ what is NOT

Exactly the review's owed list, ⛔ nothing wider:

  1. The PR body — the head reference (it claims every Verification figure is measured at ecf5fc69f; the head is 588e45950fa9e08a94749637ee181121df62aa74, two commits later), the ^\+export count (5, not 2 — already conceded in comment 5729141758 without the body being amended), and "internal surface" → published.
  2. The changeset body — it declares neither the four new package-entry exports nor the data-obj-type value change from view:dashboard to plugin-dashboard:dashboard. ⚠️ The semver level is correct and ⛔ does not move; what is incomplete is the declaration.
  3. The excluded entry's own sentence — it says dropping the line re-admits the key, and the measured result is stale-indirect-registration instead. ⭐ Born false about the diff's own mechanism, and the RETIRED_FIELD_TYPES entry on main carries the identical sentence, so it is one edit away from teaching a third copy.

⛔ Explicitly out of scope, and named so nobody folds it in: the index.tsx "LAST" rationale that the review measured as going stale once objectui#9838 / objectui#9851 are in the base. The review put it under ③ and ⛔ deliberately kept it off the owed list; ⛔ this seat does not widen the PR past what the review asked for.

Route to merge, and its red lines

Once those land: strip both needs:contract-review carriers in one act, flip ready, enqueue with squash, and follow to MERGED.

  • ⛔ No approval review of any kind is issued here. This seat's write identity resolves to hotlong, which is one of the two GOVERNED_APPROVERS — so an approval from it would be self-approval. ⛔ Never.
  • ⛔ Never merged directly: this repository refuses PUT /pulls/N/merge with 405 Changes must be made through the merge queue. The queue merges it, ⛔ not this seat.
  • ⛔ The PM writes no code. Every edit above is a dev's commit in its own worktree.
  • Landing is verified on origin/main by symbol with a lit control, ⛔ never from the merged field — measured on this board to lie in both directions.

State at this reading

mergeable_state: clean; head 588e45950fa9e08a94749637ee181121df62aa74; 36 check runs enumerated against a total_count of 36 — 33 success, 3 skipped, 0 non-green. ⇒ nothing is red, and the only thing between this PR and the queue is the owed list above.


Generated by Claude Code

Exactly the three items the at-tier contract review listed as owed before
merge on this PR, nothing wider.

1. The INDIRECT_REGISTRATIONS entry for RETIRED_DASHBOARD_NODE_TYPES claimed
   that dropping its `excluded` line re-admits `view:dashboard` to the derived
   universe, that KNOWN_SCHEMA_TYPES regains it, and that a document teaching
   it turns green again. Re-measured on this head, dropping that line makes the
   entry report `stale-indirect-registration` instead: without the exclusion the
   derivation has to read the collection literal in the site file, and
   RETIRED_DASHBOARD_NODE_TYPES is IMPORTED there from `./retired-node-types`,
   so it resolves to no literal keys at all. check-doc-component-types.mjs and
   regenerate-known-schema-types.mjs --check both exit non-zero and write
   nothing; the spelling does not re-enter, and a document teaching it stays
   red for a different reason. The sentence now says that, and points at the
   two commands that re-derive it.

2. The changeset body now declares what it was missing: the four symbols this
   branch adds to the package entry's exports["."]
   (RETIRED_DASHBOARD_NODE_TYPES, RetiredDashboardNodeTombstone,
   reportRetiredDashboardNodeType, resetRetiredDashboardNodeTypeReports) and
   the data-obj-type value change on a dashboard grid. The semver LEVEL does
   not move -- `minor` was already correct; only the declaration was
   incomplete. Frontmatter untouched.

The third owed item, the PR body's stale head reference / export floor count /
"internal surface" wording, is a body edit rather than a commit.

Deliberately NOT folded in: the index.tsx "LAST" rationale comment the review
measured as going stale once objectui#9838 / objectui#9851 are in the base.
The review put it under boundary flags and kept it off the owed list.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3049.8 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-SUBrNliw.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) 545.76KB 130.74KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 215.98KB 59.97KB
fields (index.js) 249.17KB 62.83KB
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) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
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.83KB 10.95KB
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) 5.52KB 2.10KB
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.92KB 14.22KB
plugin-charts (index.js) 71.73KB 20.08KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 132.96KB 35.17KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 253.37KB 65.90KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.81KB 34.17KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.61KB 57.90KB
plugin-kanban (index.js) 48.10KB 14.94KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.05KB 21.01KB
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) 104.82KB 34.67KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
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) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
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) 4.11KB 2.06KB
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.04KB 5.36KB
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

os-dev-report

{
"issue": 9533,
"status": "done",
"branch": "claude/issue-9533-dashboard-bare-key-converge-plugin-namespace",
"pr": "#9803",
"session": "session_01UanLVj6xvbS6puBCewLr8L",
"premise_still_valid": true,
"summary": "Closed exactly the three items the at-tier contract review (comment 5731438175) listed as owed before merge on the EXISTING PR objectui#9803, on top of head 588e459 with a plain commit 96b6ee2 -- no rebase, no amend, no force-push, and no new card or branch. (1) PR body PATCHed, not committed: the stale head reference is corrected (the Verification figures are measured at ecf5fc6 and at nothing later; the branch has since moved to 588e459 and then 96b6ee2, and the body no longer claims they were re-measured), the ^+export floor goes 2 to 5 with all five lines named, and 'a NEW internal surface no consumer had' is replaced by the accurate reading that the four symbols are PUBLISHED through the package entry's exports['.']. Body sent with ZERO footers; the platform appended exactly one, verified by reading the body back (footer count 1, and the sent bytes round-trip identical apart from that appended footer). (2) The changeset body now declares the four new package-entry exports and the data-obj-type value change; frontmatter untouched and the semver level stays minor. (3) The RETIRED_DASHBOARD_NODE_TYPES excluded sentence in INDIRECT_REGISTRATIONS is corrected to what dropping the line actually does, re-measured here before it was written. Deliberately NOT folded in, as instructed: the index.tsx 'LAST' rationale comment that goes stale once objectui#9838 / objectui#9851 are in the base -- it is untouched. No label was added, removed or read-modified; the PR is still draft and no review was submitted.",
"tests": "Re-measurement of owed item 3, done BEFORE writing the correction, via ablation-replace with anchor-hit + blob proof and an automatic restore proven by 'git diff HEAD' empty: with the RETIRED_DASHBOARD_NODE_TYPES 'excluded' line disabled, check-doc-component-types.mjs exits 1 with 'packages/plugin-dashboard/src/index.tsx (RETIRED_DASHBOARD_NODE_TYPES) [stale-indirect-registration] the collection RETIRED_DASHBOARD_NODE_TYPES no longer resolves to a non-empty list of literal keys', and regenerate-known-schema-types.mjs --check exits 1 with the same finding and writes nothing; the derived universe stays at the same key count and the WITHHELD counter goes 2 to 1. So the key does NOT re-enter and a document teaching it does NOT turn green -- exactly what the review measured. Baseline before the ablation: doc-types exit 0, '2 collection(s) declared and WITHHELD', '45 table key(s) judged, 45 registered'. Gates at head 96b6ee2, each exit code captured to a file BEFORE any pipe, all exit 0: check:doc-types, check:registry-bare-names ('0 contested; ledger holds 0'), check:changeset-claims, check:changeset-presence, check:changeset-no-major, check:pending-changeset-literals, check:control-bytes, check:new-line-citations, check:doc-fences, check:doc-example-ids, check:comment-mask-corpus, check:shell-escape-residue, check:unreferenced-sources, regenerate-known-schema-types.mjs --check. NOTE: check:registry-bare-names, check:comment-mask-corpus and check:unreferenced-sources first answered ERR_MODULE_NOT_FOUND / 'PREREQUISITE NOT MET' in the fresh worktree -- that is a missing prerequisite, not a red gate; all three were re-run after 'pnpm install' and exit 0. Tests (repo-root invocation, paths relative to the repo root, no -t and no bare '--'): 'pnpm exec vitest run scripts/tests/check-doc-component-types.test.ts scripts/tests/known-schema-types-derivation-5115.test.ts packages/cli/src/tests/retired-view-dashboard-key-9533.test.ts' -> exit 0, 'Test Files 3 passed (3), Tests 136 passed (136)' -- a real count, not a skipped run. Lint read for the word 'error', not only for exit 0: 'pnpm exec eslint scripts/check-doc-component-types.mjs' exit 0 with empty output and 0 occurrences of 'error'; '--format json' confirms the file was actually judged (1 file, errorCount 0, warningCount 0) rather than silently ignored. Control bytes: check:control-bytes exit 0 plus a manual grep -naP over both edited files and over the PR body text (no match). Export floor re-measured independently: 'git diff 42234b1..588e459 | grep -c ^+export' = 5 (four in retired-node-types.tsx, one 'export {' block in index.tsx), and 5 again at head -- the review's figure reproduces. Sibling measurement, report-only (see out_of_scope_findings): the same ablation on the RETIRED_FIELD_TYPES entry yields 'packages/fields/src/index.tsx (RETIRED_FIELD_TYPES) [stale-indirect-registration]', doc-types exit 1; the tree was restored and 'git status --porcelain' is empty. CI at head 96b6ee2, read ONCE and not polled: 36 check runs enumerated against a total_count of 36 -- 22 success, 3 skipped, 11 in_progress, 0 non-green. NOT MEASURED by me: the full suite, any build, and CI's own convergence -- I do not wait on CI.",
"mcp_calls": "0 -- no MCP GitHub tool was called, read or write",
"api_writes": "2 REST proxy writes: PATCH /repos/objectstack-ai/objectui/pulls/9803 (body only; HTTP 200, body read back and verified), POST /repos//issues/9803/comments (this report). Plus one git push of a single plain commit to refs/heads/claude/issue-9533-dashboard-bare-key-converge-plugin-namespace (588e459..96b6ee2, fast-forward, no force). NO label write of any kind -- the needs:contract-review carriers were not touched, not added and not removed; no ready flip, no enqueue, no auto-merge, no merge, no review submitted.",
"open_questions": [],
"out_of_scope_findings": [
"to file (3 classes, dedupe words: INDIRECT_REGISTRATIONS excluded RETIRED_FIELD_TYPES stale-indirect-registration field:owner) -- class (a), a reproducible defect in prose that a gate falsifies: the RETIRED_FIELD_TYPES entry in scripts/check-doc-component-types.mjs carries the identical born-false sentence ('DROP this excluded line and the collection's keys (today field:owner) enter the universe, so a document teaching field:owner turns GREEN'). Measured here with the same ablation: dropping it reports 'packages/fields/src/index.tsx (RETIRED_FIELD_TYPES) [stale-indirect-registration]' and check:doc-types exits 1 -- the keys do not enter, because RETIRED_FIELD_TYPES is IMPORTED into that site file from @object-ui/core. NOT fixed here by instruction (different file surface, outside this PR's owed list). Successor: whoever settles objectui#9717, which already records that flipping it is not a one-line change.",
"noted, not filed: the index.tsx 'LAST' rationale comment ('register() clears the lazy stub of the bare name as well as of its own full type'), true at this base and false once objectui#9838 / objectui#9851 are in the base. The contract review put it under boundary flags and deliberately kept it OFF the owed list, and the takeover note named it as explicitly out of scope -- so it is LEFT ALONE and untouched by this commit. Successor: the next PR that merges today's main into this branch, or whoever lands the follow-up to objectui#9851.",
"noted, not filed: the banned cross-file line addresses in the mdx and README this branch regenerated (Registry.ts plus a line number, already flagged by the dev and agreed out of scope by the review, and now doubly stale after objectui#9838 / objectui#9851). Untouched; check:new-line-citations is differential and exits 0 on this branch. Successor: none -- repair opportunistically per AGENTS.md when someone next edits those files."
]
}


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Clause-② contract review RE-RUN at the new head and ADOPTED — the record follows in the next comment, verbatim. domain:spec @ objectui execution seat · session_01UanLVj6xvbS6puBCewLr8L · 2026-09-18T15:09Z

Why a second record

The prior record (comment 5731438175) named head 588e45950fa9e08a94749637ee181121df62aa74. Closing its owed list moved the head to 96b6ee2820b0d67b24fab5702c921053bc332d01, and check-clause2-carriers --pair 9803 then returned no review-of-record row at all — the record no longer names the head.

⭐ That is the head-binding doing its job, ⛔ not a formality: a record is a statement about a tree, and the tree moved. ⚠️ And a body-only or prose-only delta is ⛔ not exempt — this repository's squash commit message carries the whole PR body into main's permanent history, so a sentence added to discharge a finding can itself become a durable false claim.

⛔ This seat did not judge whether the delta preserved the verdict. That judgement is itself a clause-② judgement, and this seat serves claude-opus-5, below CONTRACT_REVIEW_TIER. The question went back to an at-tier reviewer, which was given the prior record as a prior ruling it could refine or contradict, and was ⛔ explicitly not told the delta was harmless.

The tier reading — from the subagent's OWN transcript, ⛔ never get_session, ⛔ never self-report

transcript assistant records served model
this re-review 59 59 / 59 claude-fable-5-1 — the CONTRACT_REVIEW_TIER constant, zero fallback
⭐ FIRING CONTROL — the dev that produced this very delta 99 99 / 99 claude-opus-5
the prior at-tier record, for comparison 96 96 / 96 claude-fable-5-1

⇒ the control reads the other value, so the field discriminates and the at-tier reading is a reading. ⭐ Note what the control is here: the author of the diff under review — different tier, different transcript, measurably not the same agent.

⚠️⚠️ The record escalates something to the maintainer, and this seat is NOT ruling on it

Under ③ the reviewer names, unprompted, that commit 96b6ee2820's Claude-Session: trailer is this same session — the session that commissioned both records and adopts them. Its words: 「the isolation is the subagent's … not the session's」, and it asks whether a record whose Reviewed-by session equals the reviewed commit's Claude-Session satisfies the independence pair.

⛔ This seat does not answer that, and ⛔ it does not answer it by landing. Three things are true and are stated rather than blended:

  1. Mechanically the pair is satisfied as designed. references/contract-review.md reads 「Implemented-by: … mode:subagent 记分支」 and 「mode:subagent 席内审是设计」 — under subagent mode the implementer is recorded as a branch, so the SELF-REVIEW condition (both fields naming the same session) cannot arise by construction, and in-seat review is the ruled route, selected by the maintainer in session.
  2. ⚠️ The reviewer's point is sharper than the mechanism, and it is right: for this delta one session commissioned the dev, commissioned the review, and adopts it. What is isolated is the reviewing subagent, ⛔ not the session.
  3. ⭐ It is therefore a question about the ROUTE, ⛔ not about this pull request — it holds identically for every card this seat has landed through the ruled route. ⇒ singling this PR out would be arbitrary, and ⛔ withholding it would not answer the question either.

⇒ Raised to the maintainer as a standing-route question, quoted above so it is not discovered later. ⛔ Not filed as a card: this seat's REST issue-creation channel is currently refused by the session's write classifier, and ⛔ routing it through another agent would launder that refusal.

⚠️ One flag the record names and this seat is deliberately NOT acting on

The body sentence 「Registry.registerLazy has no collision check」 is true of the tree under review and stale on today's main (objectui#9838 landed since). The reviewer classifies it 「Stale on main, true of this tree」, keeps it off the owed list — as the prior record did — and observes that a one-clause repair is cheap.

⛔ This seat is not taking it, and the reason is the one this round already paid for: the body was just verified by this record, and editing verified content after the record without a further review is exactly how a record stops describing what it names. ⇒ recorded here instead, so the successor sees it and the history shows the choice.

What happens next, and its red lines

Both needs:contract-review carriers are stripped in one act, then ready → enqueue with squash → follow to MERGED.

  • ⛔ No approval review of any kind — this seat's write identity resolves to hotlong, one of the two GOVERNED_APPROVERS.
  • ⛔ Never merged directly: this repository answers PUT /pulls/N/merge with 405 Changes must be made through the merge queue.
  • ⚠️ The record's own CI reading was taken with 4 Test shards still in progress. ⛔ Never enqueue while a required check runs — the enqueue below is gated on a fresh complete enumeration, ⛔ not on the record's snapshot.
  • Landing is verified on origin/main by symbol with a lit control, ⛔ never from the merged field.

Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 96b6ee2820b0d67b24fab5702c921053bc332d01

Instrument note, first. The shared checkout now holds all three objects (42234b1a base, 588e459 prior head, 96b6ee2820 head; git merge-base of head and origin/main is 42234b1a, and 588e459's second parent is 42234b1a, so the PR base is the true merge-base). Base and head trees were extracted with git archive into scratch — nothing was written in the checkout — and the three node-only gates were run on both, exit codes written to disk before any pipe. Base tree is the lit control throughout. ⛔ NOT measured by me: vitest (no node_modules anywhere I may write), eslint, the CLI check run, the live-Registry reproduction, the built-tree gates (sdui-registration-pins, eager-closure), check-clause2-carriers --pair, and behaviour after merging today's main (mergeable_state: behind; reasoned below, not run). CI at head, read twice and not polled further: 36 runs, 29 success, 3 skipped, 4 Test (shard N/4) still in_progress, 0 failure — so the vitest figures the body cites for head rest on CI that had not finished at my last reading.

The delta 588e459..96b6ee2820, in full: two files, 31 insertions, 3 deletions. (a) .changeset/9533-…md gains two paragraphs (four published symbols; data-obj-type value), frontmatter untouched. (b) scripts/check-doc-component-types.mjs: the excluded: string of the RETIRED_DASHBOARD_NODE_TYPES entry is rewritten; no key, site, collection or reason field moves. No executable source, no test, no doc row. Plus the PR body PATCH. Judged on its own terms below; it moves no accept set (the derived universe is 648 with findings 0 on the head tree, the whitelist --check exits 0, and excluded is truthy before and after the rewrite, which is all the derivation reads of it).

① Derived judgments

  1. Renderer full name moves view:dashboard → plugin-dashboard:dashboard. Re-verified at head: namespace: 'plugin-dashboard' in index.tsx 7 (base) → 8 (head); namespace: 'view' 1 → 1 (the renderer's at base, the tombstone loop's at head). check-registry-bare-name-collisions on the head tree exit 0, 0 contested; ledger holds 0; base control exit 0, 1 contested; ledger holds 1. KNOWN_BARE_NAME_COLLISIONS is [] with the row deleted, not reworded. Right, and letter 1.

  2. view:dashboard retires to a by-name tombstone, loop-registered last with namespace: 'view', skipFallback: true; renders role="alert", data-retired-node-type, the table's own text; console.error of the same string; the pin asserts identity, text read from the table, the log, the absence of .grid.auto-rows-min, plus a lit control that bare dashboard and plugin-dashboard:dashboard still paint it. Carried from the prior record by source reading (source is byte-identical between 588e459 and head for every file but the two above); not run.

  3. Derived universe 649 → 648; the CLI accept set narrows by one key. Measured with deriveRegistryKeys(root) on both trees: view:dashboard 1 → 0; lit controls dashboard 1/1, plugin-dashboard:dashboard 1/1, view:chart 1/1, field:user 1/1; absent control field:zzznosuch 0/0; findings 0 both. known-schema-types.ts loses exactly one line; regenerate-known-schema-types --check exit 0 on both trees. Right. The prior record's 3(b) — authoring-time refusal carries no migration pointer, only the generic Unknown schema type — carried; the new CLI pin asserts exactly that string, so it is by design.

  4. The excluded entry's sentence — the delta's substantive edit. Owed item, discharged, and the discharging sentence is true as measured: on a scratch copy of head with that one excluded: property removed (count 2 → 1), check-doc-component-types exit 1 reporting packages/plugin-dashboard/src/index.tsx (RETIRED_DASHBOARD_NODE_TYPES) [stale-indirect-registration], WITHHELD count 2 → 1, universe still 648; regenerate-known-schema-types --check exit 1 with the same finding and the whitelist byte-identical to head's. Every clause of the new sentence (does NOT re-admit; both commands exit non-zero; nothing written; stays red for a different reason; not a one-line change) reproduces. It also obeys AGENTS.md 完善设计器的每一个细节 #9 — it points at the two commands rather than storing their answer. The coupling to objectui#9717 (still the open question the sibling RETIRED_FIELD_TYPES entry names) is unchanged and remains provisional. Right in text and in effect now.

  5. Four symbols added to the published entry. ^\+export against merge-base 42234b1a: 5 at head and 5 at 588e459 (export { block in index.tsx plus four in retired-node-types.tsx), exit 0 captured. package.json at head: exports["."] → ./dist/index.js / index.umd.cjs / index.d.ts, the build of src/index.tsx, so they are published, as the body now says. @object-ui/fields does export RetiredFieldTombstone from its entry, so the precedent the changeset cites is real. Owed item discharged; the changeset paragraph names all four. Right.

  6. data-obj-type on the dashboard grid now reads plugin-dashboard:dashboard. Owed item discharged in the changeset, and its claim that the tombstone publishes data-retired-node-type and no data-obj-type matches the tombstone source (it spreads nothing). Census re-run on origin/main (git grep -l view:dashboard, exit 0): 12 files — the PR's own nine targets, plugin-dashboard/CHANGELOG.md, and two new since the base, core/src/registry/Registry.ts (a comment) and core/src/registry/__tests__/Registry.test.ts (fix(core): report a bare-name collision at both registry doors, across both tables #9838's own registrations, no import of @object-ui/plugin-dashboard). No consumer keyed on the DOM value. Lit control view:chart 3 files. Right; declared.

  7. Runtime getKnownTypes() still holds view:dashboard (tombstone is a real registration) while CLI and doc gate refuse it. Carried; not measured.

  8. Docs and gates. check-doc-component-types on head: exit 0, 648 registered key(s), 2 collection(s) declared and WITHHELD, 45 table key(s) judged, 45 registered; base control: 649, 1 collection(s) … WITHHELD, 45/45. mdx row, README twin, doc-gate pin, OPEN_REGISTRATION_SITES entry: carried. Right.

  9. Test-only moves (dashboardAuthoredInputs row, sweep target, OWNED_VIEW_ALIASES emptied with its reason). Carried. Right.

PR body as it now stands — verified: floor 5 at both heads ✓; merge-base 42234b1a ✓; "declared in the changeset as of 96b6ee2820" ✓; the owed list named matches comment 5731438175 ✓; "dropping that line yields stale-indirect-registration (both commands exit 1, nothing written)" ✓ reproduced; ledger 0 ✓; check:doc-types / check:registry-bare-names / regenerate … --check exit 0 at head ✓ measured; the body no longer claims the 143/1806 figures for any head later than ecf5fc69f ✓; both needs:contract-review carriers present ✓. Not measured: the other gates it lists at head, the eslint reading, the 3-file/136-test run (CI shards in progress), --pair 9803 exit 0. Imprecise, not false: "moved twice: 588e45950f (the one-line deletion … plus a merge of main)" — the deletion is its own commit bf860c94 (1 file, 1 deletion) and 588e459 is the merge on top of it; accurate as a head reference, conflated as a commit description. Stale on main, true of this tree: "Registry.registerLazy has no collision check … a different card" — true at base 42234b1a, false on origin/main since #9838 (276d17459c). The prior record listed this as stale and kept it off the owed list; the delta left it. See ③.

Prior-record judgments re-verified at head: 1, 3, 4, 5, 6, 8 and the gate figures. Carried without re-running: 2, 7, 9, the ablation leg counts, and every vitest-backed claim.

② Semver level

minor for @object-ui/plugin-dashboard and @object-ui/cli, 「Breaking for authored metadata」 written out, no major anywhere — matches AGENTS.md 版本号策略 (objectui's own breaking changes take minor; major is forbidden and CI-enforced; the fixed group moves together so the test-only app-shell edit needs no entry). Changeset Bump Policy, Changeset Declaration, Changeset Claim Re-read and Changeset Fixed Group Check all success at head. Level correct, and the declaration is now complete: breaking tombstone, four published exports, data-obj-type value. The prior record's note stands: under objectstack#18885's addendum (packages/types/packages/spec/skills/ — this diff touches 0 files under each of the three, counted on the 15-file list) the clause would read no; that is a carrier question for objectstack#18903's landing, not a defect of this diff.

③ Boundary flags

  • Found, unflagged by anyone: this is a self-review chain. Commit 96b6ee2820's Claude-Session: trailer is session_01UanLVj6xvbS6puBCewLr8L — the same session that commissioned the prior record, that record's Reviewed-by, and this record's. The isolation is the subagent's (this brief carried no dispatch order and no seat conclusions, and every judgment above was re-measured from primary sources), not the session's. Escalated by name to the maintainer: whether a record whose Reviewed-by session equals the reviewed commit's Claude-Session satisfies the independence pair, or whether the delta needs a record from a session that did not author it. I do not rule on it; I state it so nobody discovers it later.
  • Stale body sentence, durable after squash. "Registry.registerLazy has no collision check" enters main's history describing a registerLazy that main no longer has (fix(core): report a bare-name collision at both registry doors, across both tables #9838 landed 09:00Z, before the 14:57Z body PATCH). Same disposition as the prior record — not owed, not verdict-flipping, because it is true of the tree under review — but a one-clause repair ("landed since as objectui#9838") is cheap and the claiming seat should take it before the squash.
  • index.tsx "LAST" rationale goes stale on merge. Re-verified on origin/main Registry.ts: the bare-stub delete now sits inside the fallback branch and a skipFallback registration clears nothing, so "register() clears the lazy stub of the bare name as well as of its own full type" is false there. Post-merge pin behaviour, reasoned not run: both replay orders stay warning-free (claimedBy === fullType at both doors, and the tombstone never enters the guard), and the stub-clearing case stays green (the real registration takes the bare key and clears its stub in-branch). Kept off the owed list by the prior record and by the takeover note; carried as such.
  • Dev: registerLazy unguarded door, different card. Agree, and landed (fix(core): report a bare-name collision at both registry doors, across both tables #9838, fix(core): a registration that declines the bare key no longer clears its stub #9851). Both post-date this head; the PR is behind.
  • Dev: the excluded disposition as a design point. Execution detail jointly fixed by the ruling and bug(console,docs): line-chart / area-chart / advanced-chart are lazy stubs @object-ui/plugin-charts never fulfils — the dashboard doc teaches one, and it paints OBJUI-001 #8760; escalated by name to objectui#9717, which now owns two entries. The sibling RETIRED_FIELD_TYPES sentence on main remains born-false (dev measured it, out of scope here); successor is finding(tooling): field:owner is registered at runtime and missing from the generated universe — the indirect bypass is keyed by FILE while the table's coverage is keyed by COLLECTION #9717's settler.
  • Dev: README twin; apps/console untouched; serial scope extended to app-shell test, cli test, scripts; packages/types untouched. Agree; 0 files under packages/types, packages/spec, skills/ re-counted.
  • Dev out-of-scope: cross-file line addresses Registry.ts:194 / :226 in mdx and README. The diff rewrites the lines carrying them, so they appear as + lines; Line Citation Gate success at head, evidently not scoring a citation the base already held. Not measured by me (the gate needs a git base and the checkout is read-only). Out of scope for a scope-pinned card; doubly stale after fix(core): report a bare-name collision at both registry doors, across both tables #9838/fix(core): a registration that declines the bare key no longer clears its stub #9851.
  • Dev out-of-scope: OWNED_VIEW_ALIASES empty. Agree; reason written at the entry.
  • Prior record ①3(b): authoring-time refusal has no pointer. Carried; satisfied as ruled.
  • Prior record ②: objectstack#18885 addendum. Carried as a carrier question.
  • Owed list, disposition: all three discharged at head, and each discharging sentence re-measured true — the excluded sentence by ablation, the changeset paragraphs against package.json and the tombstone source, the body against the diff and the commit graph. Nothing new is owed by this record; the two flags above (stale registerLazy sentence; self-review chain) are named, not owed.

Implemented-by: claude/issue-9533-dashboard-bare-key-converge-plugin-namespace
Reviewed-by: session_01UanLVj6xvbS6puBCewLr8L

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Clause-② gate CLEARED — both carriers stripped in this act. domain:spec @ objectui execution seat · session_01UanLVj6xvbS6puBCewLr8L · 2026-09-18T15:10Z

what value
review of record comment 5731997344 on this PR
head it judges 96b6ee2820b0d67b24fab5702c921053bc332d01
verdict PASS
served tier CONTRACT_REVIEW_TIER, read 59 / 59 from the reviewer's own transcript, firing control 99 / 99 the other way
machine read check-clause2-carriers --pair 9803 exit 0 — the declaration is readable in the fixed spelling, both carriers agree, and C6-RECORD names this head
superseded record comment 5731438175, which named head 588e45950f and is ⛔ no longer the record of this head

⛔ needs:contract-review is removed from both carriers — this PR and card objectui#9533 — in one act, seconds apart. ⭐ That pairing is the only machine-readable evidence that distinguishes a legitimate CLEAR from a label someone stripped: a clear leaves two removals close together, a strip leaves one. ⚠️ And 「a gate label being stripped is not a red light, it is a green one」 — which is exactly why the distinction has to be legible.

⛔ What clearing the gate does NOT mean

  • ⛔ It is not an approval. ⛔ No review of any kind has been or will be submitted here: this seat's write identity resolves to hotlong, one of the two GOVERNED_APPROVERS.
  • ⛔ It is not an enqueue. At this reading CI is 36 runs enumerated against a total_count of 36 — 31 success · 3 skipped · 2 Test shards still in progress · 0 non-green. ⛔ Never enqueue while a required check runs; the enqueue will be gated on a fresh complete enumeration, ⛔ not on this snapshot.
  • ⛔ It does not settle the independence question the record raises under ③. That is a standing-route question, raised to the maintainer and ⛔ not answered by this act — see the adoption note above.

Route from here: ready → enqueue with squash → follow to MERGED via the queue, ⛔ never a direct merge (this repository answers PUT /pulls/N/merge with 405). Landing verified on origin/main by symbol with a lit control, ⛔ never from the merged field.


Generated by Claude Code

@os-sales
os-sales marked this pull request as ready for review September 18, 2026 15:20
@os-sales
os-sales added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit e356c39 Sep 18, 2026
38 checks passed
@os-sales
os-sales deleted the claude/issue-9533-dashboard-bare-key-converge-plugin-namespace branch September 18, 2026 15:56

Copy link
Copy Markdown
Collaborator

LANDED — and this seat's follow-through ends here. domain:spec @ objectui execution seat · session_01UanLVj6xvbS6puBCewLr8L · 2026-09-18T15:59Z

Merged through the queue; squash on origin/main at tip e356c39ee7.

Verified on origin/main by symbol, ⛔ not from the merged field

probe reading
SUBJECT — namespace: 'plugin-dashboard' in packages/plugin-dashboard/src/index.tsx 8 (the base carried 7 — the one-token move the ruling asked for)
SUBJECT — the tombstone module retired-node-types present
SUBJECT — 'view:dashboard' in packages/cli/src/utils/known-schema-types.ts empty cell
⭐ LIT CONTROL — 'plugin-dashboard:dashboard' in the same whitelist 1
⭐ LIT CONTROL — 'view:chart' in the same whitelist 1

⚠️ ⛔ The view:dashboard row is deliberately not written as a 0: grep -c with no match exits non-zero and returns nothing. ⭐ The two lit controls are what make that empty cell a reading — the matcher still finds both the converged key and an unrelated namespaced key in the same file, so the whitelist genuinely lost the retired spelling.

The maintainer instruction this discharges, quoted verbatim

  1. 「9803 帮我契约复审」 → the at-tier record at comment 5731438175 (head 588e45950f).
  2. 「9803 你负责跟进到合并」 → the owed repairs, the re-review at comment 5731997344 (head 96b6ee2820), the carrier clear, the enqueue, and this landing record.

⭐ Why there are two records, and why the second was not optional: closing the first record's owed list moved the head, and check-clause2-carriers --pair 9803 then reported no review of record at all. A record is a statement about a tree, and the tree had moved. ⚠️ A prose-only delta is ⛔ not exempt — this repository's squash commit carries the whole PR body into main's permanent history, so a sentence added to discharge a finding can itself become a durable false claim. ⛔ And judging whether the delta preserved the verdict is itself a clause-② judgement, which this seat (serving below CONTRACT_REVIEW_TIER) ⛔ may not make.

⛔ What this seat did NOT do

  • ⛔ No approval review of any kind, at any point — this seat's write identity resolves to hotlong, one of the two GOVERNED_APPROVERS.
  • ⛔ Card objectui#9533 was not re-graded, re-routed or closed by this seat. It is domain:ui, assigned to os-justin; whatever Fixes #9533 did on merge is the platform's act, ⛔ not this seat's. Its lane owns everything after this.
  • ⛔ The index.tsx 「LAST」 rationale comment — which both records measured as going stale once objectui#9838 / objectui#9851 are in the base — was left untouched, because both records deliberately kept it off the owed list.
  • ⛔ The stale body clause 「Registry.registerLazy has no collision check」 was not repaired, and that was a decision rather than an oversight: it is true of the tree under review, both records kept it off the owed list, and the body had just been verified by the second record — editing verified content afterwards without a further review is precisely how a record stops describing what it names.

⚠️⚠️ Two things the second record escalated, both still OPEN and neither answered by this landing

  1. ⭐⭐ The self-review chain. Commit 96b6ee2820's Claude-Session: trailer is this same session — the one that commissioned both records and adopts them. Mechanically the independence pair is satisfied as designed (Implemented-by: records a branch under mode:subagent, and 「mode:subagent 席内审是设计」), but the reviewer's sharper reading stands: the isolation is the subagent's, ⛔ not the session's. ⇒ this is a question about the ROUTE, ⛔ not about this pull request — it holds identically for every card landed through it. Raised to the maintainer; ⛔ not answered by landing, and ⛔ not answerable by this seat.
  2. The sibling born-false sentence. RETIRED_FIELD_TYPES' entry in scripts/check-doc-component-types.mjs carries the identical false claim this PR just corrected for RETIRED_DASHBOARD_NODE_TYPES — measured by the dev with the same ablation. ⛔ Out of scope here by instruction; owed its own card, which this seat ⛔ cannot file (REST issue creation refused by the session's write classifier, and ⛔ routing it through another agent would launder that refusal). Successor named by the record: whoever settles objectui#9717, which now owns two entries.

Generated by Claude Code

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

Labels

Projects

None yet

3 participants