fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card - #7419

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface
Sep 3, 2026
Merged

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card#7419
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7115

The root README.md — the repository's GitHub landing page and the npm page for the workspace, the most-read authored file in it — was outside the scan surface of every doc gate. check-doc-component-types walked content/docs. check-doc-snippet-types walked content/docs plus the package READMEs. The root README fell between the two, and taught stat-card four times in its flagship "dashboard in JSON" example. Nothing registers stat-card, so a reader who copied the headline snippet got four OBJUI-001 "Unknown component type" panels.

The ruling this implements

Maintainer, 2026-09-01, verbatim 「同意」 (comment 5494799307), quoted in the ruled order:

  1. 门禁盲区:根 README.md 加进 check-doc-component-typescheck-doc-snippet-types 的扫描面(最高价值半边,先落——内容修了不加面就会再烂);
  2. 内容修:四个 stat-card widget 改 statistic(真实注册且有 value 运送行,"${…}" 照常求值,示例保持表达力);
  3. 叉裁 B:非 text 类型不加运送行 —— 停止在无运送行的键上编写 ${…}(文档教现实);真实应用要哪个键求值 ⇒ 带消费者立 carriage-row 卡,届时按条款②扩

All three parts, one PR (lane's choice, as the ruling permits).

Part 1 — the gate blind spot, proved red-then-green

Widening a scan surface is exactly the change that can be green about nothing, so the widening was landed red first and the evidence is here rather than asserted.

check-doc-component-types (ROOT_PAGES = ['README.md'], joined to scanDocs's file list):

statefiles scannedexitwhat it said
before, unwidened1850green — the README was not in the walk
widened, content untouched18617 findings, 4 of them the ruled ones
widened + content fixed1860green

The failure that proves the walk reaches the file, verbatim from the run:

 README.md:290 [unregistered-doc-type] type 'stat-card' (json)
{ "type": "stat-card", "title": "Revenue", "value": "${stats.revenue}", "trend": "+12%", "w": 3, "h": 1 },
README.md:291 [unregistered-doc-type] type 'stat-card' (json)
README.md:292 [unregistered-doc-type] type 'stat-card' (json)
README.md:293 [unregistered-doc-type] type 'stat-card' (json)

Ablation, to show the pins can fail. With the implementation committed, the files.push(...ROOT_PAGES…) line was deleted (anchored grep 1 to 0, blob 6688e098 to d0441c22 — confirmed on disk before the run, not asserted). The gate went back to 185 files and printed ✅ Every documented component type is registered — the exact "green about nothing" state — and both new pins went red with the intended message. Restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and the restore is verified by state: git diff HEAD empty and the blob equal to the HEAD blob, not by an exit code.

check-doc-snippet-types — the root README does carry snippets its rules cover (five tsx/typescript blocks), so the same proof applies and is not hand-waved. Widening it took the scan set from 224 to 225 documents, and the build closure the gate computes for itself grew by exactly @object-ui/providers — a second, independent signal that the walk now reaches the file. It went red with 9 semantic diagnostics.

⚠️Read this half honestly: those diagnostics are declared debt, not fixed.README.md is now named in UNGATED_DOCS with a measured reason, which is that gate's own designed mechanism and the objectui#5174 distinction its header states — a document outside the walk is "neither covered NOR declared ungated", invisible to the gate's own accounting, whereas a ledgered one is named, counted, re-derived every run and shrink-only. Three of the nine are real defects (ObjectRenderer, registerDefaultRenderers, createObjectStackAdapter attributed to the wrong package) and are filed as #7417, with the reason string in the ledger naming that card. Making those blocks compile means resolving two phantom APIs, which is authoring work with an API question inside it — not this card.

The third gate — check-doc-fence-languages, found by its own coupling pin

The ruling named two gates. There are four.check-doc-fence-languages re-implements the snippet gate's walk (deliberately, so it needs no install — the snippet gate imports typescript, and an install-gated docs check is one a docs-only PR skips), and check-doc-fence-languages.test.ts asserts fenceDocuments(ROOT)equalssnippetDocuments(ROOT). Widening one surface without the other breaks that coupling by construction, and it did: the pin went red at 223 vs 224 documents.

The pin was not touched — it is doing precisely the job it was written for, and it is the only reason the third gate was found at all. The fence guard's surface is widened instead, and its header now states the surface where a reader will find it, as objectui#5174's own finding requires.

ROOT_PAGEScannot be shared by import: anything imported from check-doc-snippet-types.mjs pulls in its import ts from 'typescript' at load, which would install-gate the one gate whose value is running install-free. So it stays a copy, on the bargain the document walk and TS_FENCE_LANGUAGES already make — copy freely, compare always. All three now exportROOT_PAGES and a new pin compares all three, so the shared half cannot drift even where the full surfaces legitimately differ (component-types deliberately does not walk the package READMEs, so it cannot join the list-equality comparison).

The root README needed no fence fix, and that green is not vacuous. With it in the walk the guard reports 225 documents and zero findings for it; the objectui#5867 shrink-only baseline is untouched, with no entry added. Control, through the shipped census(): un-fencing one of the README's tsx blocks produces { rel: 'README.md', line: 206, language: '', mode: 'synonym' }, so the file is genuinely parsed and genuinely judged.

Four gates, measured rather than assumed

The card's thesis was that the most-read authored file in the repository was checked by nothing. The true number turned out to be larger than the ruling enumerated, which is the thesis proving itself a second time:

gatestatus
check-doc-component-typeswidened here
check-doc-snippet-typeswidened here
check-readme-exportssame blind spot, surface is packages/NAME/README.md only — filed as #7417
check-doc-fence-languageswidened here, found by its own coupling pin rather than by anyone looking

Part 2 — the content fix

The four widgets become statistic, registered at packages/components/src/renderers/data-display/statistic.tsx:64. ⭐ A retarget, not a downgrade to literals: measured from the built artifact (expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes), statistic carries [label, value, description], so "value": "${stats.revenue}" keeps evaluating.

Two keys moved with the type, because the renderer's real shape is not stat-card's: it reads schema.label (not title), and its trend is the enum up/down/neutral with the delta text in description. "trend": "+12%" would have rendered no arrow and no text — 文档教现实 applies to the retarget too.

Part 3 — fork ruled B, and no carriage row was added

Zero carriage rows added. The map lives in @objectstack/spec (a dependency), and this PR touches neither it nor SchemaRenderer.tsx.

Every ${…} rewritten, with the measured row set for its node type:

filenode typekeycarriage rows for that typenow
content/docs/guide/expressions.md (Percentage Bar)progressvaluenoneliteral 75, with visibleOn carrying the expression
content/docs/guide/expressions.md (Percentage Bar)progresslabelnoneliteral — same node, same defect, named on the card as :388
content/docs/guide/expressions.md (Computed Fields)inputvaluenoneshown by a text node's content, which is evaluated on every type
packages/plugin-dashboard/README.mdmetric-cardvaluenoneliteral, with a sentence pointing at the object-* widgets that do read live data
packages/plugin-dashboard/README.mdmetric-cardtrendnoneliteral 'up'
packages/react/README.mdinputvaluenonemoved to a sibling text node's content

On the card's own second-order note: it recorded, unmeasured, that metric-card and input "likely render the literal ${...}". That is now measured — both carry the empty set — but the rendering claim itself was never put in a browser and is not restated here as one.

⚠️ Deviations — flagged, not folded in silently

Widening a surface finds what the surface was hiding. Four things surfaced that the ruling's enumeration does not name, because the census behind it was taken before any gate had read this file. Each is stated so a reviewer can drop it on its own.

  1. crud × 2 — a RETIRED type. The widened gate found README.md:137 and :264 teaching crud. CRUDSchema and the crud node type were removed in objectui#5373 under ADR-0049; content/docs/api/schema-reference.md:541-:563 carries the retirement and an explicit "what to author instead" table, and validateSchema already returns RETIRED_TYPE for the name. The README was teaching a type the platform refuses by name. Retargeted per that table: the UserForm comparison onto object-form, the "Data Grid" example onto object-grid with that schema's real keys (field, not name, as its own doc row insists).
  2. submit — not a node type.README.md:256 authored "actions": [{ "type": "submit" }] under a form. FormSchema.actions is SchemaNode[], so this is a rendered node position, and ActionSchema.type is 'action' — the authentication.mdx exemption for submit does not reach here. The declared key for the built-in submit button is submitLabel, which is what it now says.
  3. card.value × 3 — ruling B, in a site the ruling's census never read. The "Basic Usage" snippet bound ${stats.*} in card.value; card carries [title, description], so value there is a literal. Retargeted onto statistic — the same move part 2 ordered, for the same reason.
  4. Findings left alone and filed rather than fixed: finding(docs): the root README's ts/tsx snippets do not compile — 3 names on no built dist/index.d.ts, incl. createObjectStackAdapter attributed to the wrong package #7417 (the root README's snippet debt, plus check-readme-exports' identical blind spot) and finding(docs): guide/expressions.md still authors ${…} in 6 more keys with no carriage row — badge.text / badge.variant / card.className / card.body, beyond the two the 2026-09-01 ruling named #7418 (six more non-carriage ${…} in guide/expressions.md beyond the two the ruling enumerated).

Items 1-3 are the card's own defect classes in the card's own file, and 1-2 are structurally forced: part 1 cannot land green while the file it now reads is red. They are bounded, and each correct form is pinned by evidence already in the tree — nothing here is a judgement call about an API.

Verification

All runs at 02efff4ba, the final commit, with a clean tree.

checkresult
check:doc-types✅ 186 files, 922 type literals, 787 registered
check:doc-snippets✅ 225 documents, 193 covered, 32 ungated, 417/417 blocks judged
check:doc-fences225 documents, root README contributing 0 findings (control fires — see above)
docs:check-links✅ 17 scan roots
check:readme-exports✅ 406 self-imports judged, 0 fabricated
check:control-bytes✅ 6109 files
check:pre-install-import-graph✅ 22 modules walked, every non-relative leaf a node builtin — the fence guard stays install-free
changeset:check + check-changeset-presence✅ — "no changeset is owed"; an empty-frontmatter declaration is here anyway, per the objectui#7114 precedent
the three gate suites + 7 derived tests✅ 10 files, 226 tests (the three gate suites carry +10 new pins)
eslint --no-inline-config over the changed lintable files✅ 0 errors, 0 warnings

On the eslint narrowing, stated so it reads as a measurement rather than a skip: ① the receiving population is read from eslint.config.js itself (files: ['**/*.{ts,tsx}'] plus narrower overrides; scripts/ is not in ignores) — the .mjs files are outside it and were passed explicitly, so this over-covers; ② the count comes from --format json: 0 errors, 0 warnings; ③ eslint.config.js has zero occurrences of projectService / parserOptions / project: (control: languageOptions matches once), so type-aware linting is off and no untouched file's verdict can depend on this diff.

check:eager-closure is unmoved, and this is structural rather than a byte reading. Locally the gate reports its own precondition — "No eager-closure report at apps/console/dist/eager-closure.json … a broken gauge, not a passing budget" — so it is NOT MEASURED here, not green. What is measured: 0 files in this diff are under apps/** or packages/*/src/**; the rest are scripts/ CI tooling and markdown. No source imports a README or a scripts/ file (the only ?raw imports in the tree are apps/console/index.html inside console tests; control: the same probe over d4c6a86a1 lists four real src/ files). Nothing here is a bundler input. CI confirms it: Bundle Analysis is green on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

…and stops teaching `stat-card`
The root `README.md` was outside the scan surface of EVERY doc gate.
`check-doc-component-types` walked `content/docs`; `check-doc-snippet-types`
walked `content/docs` plus the package READMEs; the most-read authored file in
the repository fell between the two. It taught `stat-card` four times, in the
flagship "dashboard in JSON" example, and `stat-card` is registered nowhere: a
reader who copied the headline snippet got four OBJUI-001 panels.
Maintainer ruling 2026-09-01 (verbatim 「同意」), in the ruled order:
1. The gate blind spot lands first — 「内容修了不加面就会再烂」.
2. The four widgets become `statistic`, which is registered
(`statistic.tsx:64`) and declares a `value` carriage row, so the example
keeps its expressions rather than degrading to literals.
3. The (A)/(B) fork is ruled B: non-`text` types get NO new carriage rows, and
the docs stop authoring `${…}` in keys that have none — 「文档教现实」.
Red-then-green, because a widened surface can be green about nothing: with
`ROOT_PAGES` added and the content untouched, `check-doc-component-types`
failed on `README.md:290`-`:293` naming `stat-card`; the content fix turned it
green. The same widening on `check-doc-snippet-types` red-flagged nine semantic
diagnostics, which are declared as measured debt in `UNGATED_DOCS` and filed as
objectui#7417 — the ledger names the file instead of the walk missing it.
Beyond the ruling's enumeration, and flagged rather than folded in silently:
`crud` (retired in objectui#5373) and `submit` were the other unregistered types
the widened gate found in the same file, and `card.value` carries no carriage
row. All four are the card's own defect classes in the card's own file; the PR
body names each with its evidence.
Fixes#7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation package: react plugin tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

…he third gate, found by its own coupling pin
`check-doc-fence-languages.test.ts` asserts `fenceDocuments(ROOT)` equals
`snippetDocuments(ROOT)` — the fence guard re-implements the snippet gate's walk
so it needs NO install, and that pin is what makes a deliberate copy safe. The
previous commit widened the snippet gate onto the root `README.md` and left the
copy behind, so the pin went red on 223 vs 224 documents.
That is the pin doing exactly its job, so it is not touched: the fence guard's
surface is widened instead, and its header now STATES the surface (objectui#5174's
own finding, applied here as it was to the other two).
`ROOT_PAGES` cannot be shared by import — anything imported from
`check-doc-snippet-types.mjs` pulls in its `import ts from 'typescript'` at load,
and this guard's whole value is running install-free in the unfiltered workflow a
docs-only PR starts. So it stays a copy, on the same bargain the document walk and
`TS_FENCE_LANGUAGES` already make: copy freely, compare always. All three gates now
EXPORT `ROOT_PAGES` and a new pin compares all three, so the shared half cannot
drift even where the full surfaces legitimately differ (component-types does not
walk the package READMEs).
⭐ The root README needed no fence fix: with it in the walk the guard reports 225
documents and zero findings for it, and the green is not vacuous — un-fencing one
of its `tsx` blocks in a census probe produces `README.md:206 language:'' mode:
synonym`. The objectui#5867 shrink-only baseline is untouched: no entry added.
This makes FOUR gates the root README's blind spot reached, now measured rather
than assumed — the card's thesis proving itself a second time.
Part of #7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 01:06
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 75e7436Sep 3, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7115-root-readme-doc-gate-surface branch September 3, 2026 01:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationpackage: reactplugintests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card - #7419

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface
Sep 3, 2026
Merged

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card#7419
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7115

The root README.md — the repository's GitHub landing page and the npm page for the workspace, the most-read authored file in it — was outside the scan surface of every doc gate. check-doc-component-types walked content/docs. check-doc-snippet-types walked content/docs plus the package READMEs. The root README fell between the two, and taught stat-card four times in its flagship "dashboard in JSON" example. Nothing registers stat-card, so a reader who copied the headline snippet got four OBJUI-001 "Unknown component type" panels.

The ruling this implements

Maintainer, 2026-09-01, verbatim 「同意」 (comment 5494799307), quoted in the ruled order:

  1. 门禁盲区:根 README.md 加进 check-doc-component-typescheck-doc-snippet-types 的扫描面(最高价值半边,先落——内容修了不加面就会再烂);
  2. 内容修:四个 stat-card widget 改 statistic(真实注册且有 value 运送行,"${…}" 照常求值,示例保持表达力);
  3. 叉裁 B:非 text 类型不加运送行 —— 停止在无运送行的键上编写 ${…}(文档教现实);真实应用要哪个键求值 ⇒ 带消费者立 carriage-row 卡,届时按条款②扩

All three parts, one PR (lane's choice, as the ruling permits).

Part 1 — the gate blind spot, proved red-then-green

Widening a scan surface is exactly the change that can be green about nothing, so the widening was landed red first and the evidence is here rather than asserted.

check-doc-component-types (ROOT_PAGES = ['README.md'], joined to scanDocs's file list):

statefiles scannedexitwhat it said
before, unwidened1850green — the README was not in the walk
widened, content untouched18617 findings, 4 of them the ruled ones
widened + content fixed1860green

The failure that proves the walk reaches the file, verbatim from the run:

 README.md:290 [unregistered-doc-type] type 'stat-card' (json)
{ "type": "stat-card", "title": "Revenue", "value": "${stats.revenue}", "trend": "+12%", "w": 3, "h": 1 },
README.md:291 [unregistered-doc-type] type 'stat-card' (json)
README.md:292 [unregistered-doc-type] type 'stat-card' (json)
README.md:293 [unregistered-doc-type] type 'stat-card' (json)

Ablation, to show the pins can fail. With the implementation committed, the files.push(...ROOT_PAGES…) line was deleted (anchored grep 1 to 0, blob 6688e098 to d0441c22 — confirmed on disk before the run, not asserted). The gate went back to 185 files and printed ✅ Every documented component type is registered — the exact "green about nothing" state — and both new pins went red with the intended message. Restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and the restore is verified by state: git diff HEAD empty and the blob equal to the HEAD blob, not by an exit code.

check-doc-snippet-types — the root README does carry snippets its rules cover (five tsx/typescript blocks), so the same proof applies and is not hand-waved. Widening it took the scan set from 224 to 225 documents, and the build closure the gate computes for itself grew by exactly @object-ui/providers — a second, independent signal that the walk now reaches the file. It went red with 9 semantic diagnostics.

⚠️Read this half honestly: those diagnostics are declared debt, not fixed.README.md is now named in UNGATED_DOCS with a measured reason, which is that gate's own designed mechanism and the objectui#5174 distinction its header states — a document outside the walk is "neither covered NOR declared ungated", invisible to the gate's own accounting, whereas a ledgered one is named, counted, re-derived every run and shrink-only. Three of the nine are real defects (ObjectRenderer, registerDefaultRenderers, createObjectStackAdapter attributed to the wrong package) and are filed as #7417, with the reason string in the ledger naming that card. Making those blocks compile means resolving two phantom APIs, which is authoring work with an API question inside it — not this card.

The third gate — check-doc-fence-languages, found by its own coupling pin

The ruling named two gates. There are four.check-doc-fence-languages re-implements the snippet gate's walk (deliberately, so it needs no install — the snippet gate imports typescript, and an install-gated docs check is one a docs-only PR skips), and check-doc-fence-languages.test.ts asserts fenceDocuments(ROOT)equalssnippetDocuments(ROOT). Widening one surface without the other breaks that coupling by construction, and it did: the pin went red at 223 vs 224 documents.

The pin was not touched — it is doing precisely the job it was written for, and it is the only reason the third gate was found at all. The fence guard's surface is widened instead, and its header now states the surface where a reader will find it, as objectui#5174's own finding requires.

ROOT_PAGEScannot be shared by import: anything imported from check-doc-snippet-types.mjs pulls in its import ts from 'typescript' at load, which would install-gate the one gate whose value is running install-free. So it stays a copy, on the bargain the document walk and TS_FENCE_LANGUAGES already make — copy freely, compare always. All three now exportROOT_PAGES and a new pin compares all three, so the shared half cannot drift even where the full surfaces legitimately differ (component-types deliberately does not walk the package READMEs, so it cannot join the list-equality comparison).

The root README needed no fence fix, and that green is not vacuous. With it in the walk the guard reports 225 documents and zero findings for it; the objectui#5867 shrink-only baseline is untouched, with no entry added. Control, through the shipped census(): un-fencing one of the README's tsx blocks produces { rel: 'README.md', line: 206, language: '', mode: 'synonym' }, so the file is genuinely parsed and genuinely judged.

Four gates, measured rather than assumed

The card's thesis was that the most-read authored file in the repository was checked by nothing. The true number turned out to be larger than the ruling enumerated, which is the thesis proving itself a second time:

gatestatus
check-doc-component-typeswidened here
check-doc-snippet-typeswidened here
check-readme-exportssame blind spot, surface is packages/NAME/README.md only — filed as #7417
check-doc-fence-languageswidened here, found by its own coupling pin rather than by anyone looking

Part 2 — the content fix

The four widgets become statistic, registered at packages/components/src/renderers/data-display/statistic.tsx:64. ⭐ A retarget, not a downgrade to literals: measured from the built artifact (expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes), statistic carries [label, value, description], so "value": "${stats.revenue}" keeps evaluating.

Two keys moved with the type, because the renderer's real shape is not stat-card's: it reads schema.label (not title), and its trend is the enum up/down/neutral with the delta text in description. "trend": "+12%" would have rendered no arrow and no text — 文档教现实 applies to the retarget too.

Part 3 — fork ruled B, and no carriage row was added

Zero carriage rows added. The map lives in @objectstack/spec (a dependency), and this PR touches neither it nor SchemaRenderer.tsx.

Every ${…} rewritten, with the measured row set for its node type:

filenode typekeycarriage rows for that typenow
content/docs/guide/expressions.md (Percentage Bar)progressvaluenoneliteral 75, with visibleOn carrying the expression
content/docs/guide/expressions.md (Percentage Bar)progresslabelnoneliteral — same node, same defect, named on the card as :388
content/docs/guide/expressions.md (Computed Fields)inputvaluenoneshown by a text node's content, which is evaluated on every type
packages/plugin-dashboard/README.mdmetric-cardvaluenoneliteral, with a sentence pointing at the object-* widgets that do read live data
packages/plugin-dashboard/README.mdmetric-cardtrendnoneliteral 'up'
packages/react/README.mdinputvaluenonemoved to a sibling text node's content

On the card's own second-order note: it recorded, unmeasured, that metric-card and input "likely render the literal ${...}". That is now measured — both carry the empty set — but the rendering claim itself was never put in a browser and is not restated here as one.

⚠️ Deviations — flagged, not folded in silently

Widening a surface finds what the surface was hiding. Four things surfaced that the ruling's enumeration does not name, because the census behind it was taken before any gate had read this file. Each is stated so a reviewer can drop it on its own.

  1. crud × 2 — a RETIRED type. The widened gate found README.md:137 and :264 teaching crud. CRUDSchema and the crud node type were removed in objectui#5373 under ADR-0049; content/docs/api/schema-reference.md:541-:563 carries the retirement and an explicit "what to author instead" table, and validateSchema already returns RETIRED_TYPE for the name. The README was teaching a type the platform refuses by name. Retargeted per that table: the UserForm comparison onto object-form, the "Data Grid" example onto object-grid with that schema's real keys (field, not name, as its own doc row insists).
  2. submit — not a node type.README.md:256 authored "actions": [{ "type": "submit" }] under a form. FormSchema.actions is SchemaNode[], so this is a rendered node position, and ActionSchema.type is 'action' — the authentication.mdx exemption for submit does not reach here. The declared key for the built-in submit button is submitLabel, which is what it now says.
  3. card.value × 3 — ruling B, in a site the ruling's census never read. The "Basic Usage" snippet bound ${stats.*} in card.value; card carries [title, description], so value there is a literal. Retargeted onto statistic — the same move part 2 ordered, for the same reason.
  4. Findings left alone and filed rather than fixed: finding(docs): the root README's ts/tsx snippets do not compile — 3 names on no built dist/index.d.ts, incl. createObjectStackAdapter attributed to the wrong package #7417 (the root README's snippet debt, plus check-readme-exports' identical blind spot) and finding(docs): guide/expressions.md still authors ${…} in 6 more keys with no carriage row — badge.text / badge.variant / card.className / card.body, beyond the two the 2026-09-01 ruling named #7418 (six more non-carriage ${…} in guide/expressions.md beyond the two the ruling enumerated).

Items 1-3 are the card's own defect classes in the card's own file, and 1-2 are structurally forced: part 1 cannot land green while the file it now reads is red. They are bounded, and each correct form is pinned by evidence already in the tree — nothing here is a judgement call about an API.

Verification

All runs at 02efff4ba, the final commit, with a clean tree.

checkresult
check:doc-types✅ 186 files, 922 type literals, 787 registered
check:doc-snippets✅ 225 documents, 193 covered, 32 ungated, 417/417 blocks judged
check:doc-fences225 documents, root README contributing 0 findings (control fires — see above)
docs:check-links✅ 17 scan roots
check:readme-exports✅ 406 self-imports judged, 0 fabricated
check:control-bytes✅ 6109 files
check:pre-install-import-graph✅ 22 modules walked, every non-relative leaf a node builtin — the fence guard stays install-free
changeset:check + check-changeset-presence✅ — "no changeset is owed"; an empty-frontmatter declaration is here anyway, per the objectui#7114 precedent
the three gate suites + 7 derived tests✅ 10 files, 226 tests (the three gate suites carry +10 new pins)
eslint --no-inline-config over the changed lintable files✅ 0 errors, 0 warnings

On the eslint narrowing, stated so it reads as a measurement rather than a skip: ① the receiving population is read from eslint.config.js itself (files: ['**/*.{ts,tsx}'] plus narrower overrides; scripts/ is not in ignores) — the .mjs files are outside it and were passed explicitly, so this over-covers; ② the count comes from --format json: 0 errors, 0 warnings; ③ eslint.config.js has zero occurrences of projectService / parserOptions / project: (control: languageOptions matches once), so type-aware linting is off and no untouched file's verdict can depend on this diff.

check:eager-closure is unmoved, and this is structural rather than a byte reading. Locally the gate reports its own precondition — "No eager-closure report at apps/console/dist/eager-closure.json … a broken gauge, not a passing budget" — so it is NOT MEASURED here, not green. What is measured: 0 files in this diff are under apps/** or packages/*/src/**; the rest are scripts/ CI tooling and markdown. No source imports a README or a scripts/ file (the only ?raw imports in the tree are apps/console/index.html inside console tests; control: the same probe over d4c6a86a1 lists four real src/ files). Nothing here is a bundler input. CI confirms it: Bundle Analysis is green on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

…and stops teaching `stat-card`
The root `README.md` was outside the scan surface of EVERY doc gate.
`check-doc-component-types` walked `content/docs`; `check-doc-snippet-types`
walked `content/docs` plus the package READMEs; the most-read authored file in
the repository fell between the two. It taught `stat-card` four times, in the
flagship "dashboard in JSON" example, and `stat-card` is registered nowhere: a
reader who copied the headline snippet got four OBJUI-001 panels.
Maintainer ruling 2026-09-01 (verbatim 「同意」), in the ruled order:
1. The gate blind spot lands first — 「内容修了不加面就会再烂」.
2. The four widgets become `statistic`, which is registered
(`statistic.tsx:64`) and declares a `value` carriage row, so the example
keeps its expressions rather than degrading to literals.
3. The (A)/(B) fork is ruled B: non-`text` types get NO new carriage rows, and
the docs stop authoring `${…}` in keys that have none — 「文档教现实」.
Red-then-green, because a widened surface can be green about nothing: with
`ROOT_PAGES` added and the content untouched, `check-doc-component-types`
failed on `README.md:290`-`:293` naming `stat-card`; the content fix turned it
green. The same widening on `check-doc-snippet-types` red-flagged nine semantic
diagnostics, which are declared as measured debt in `UNGATED_DOCS` and filed as
objectui#7417 — the ledger names the file instead of the walk missing it.
Beyond the ruling's enumeration, and flagged rather than folded in silently:
`crud` (retired in objectui#5373) and `submit` were the other unregistered types
the widened gate found in the same file, and `card.value` carries no carriage
row. All four are the card's own defect classes in the card's own file; the PR
body names each with its evidence.
Fixes#7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation package: react plugin tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

…he third gate, found by its own coupling pin
`check-doc-fence-languages.test.ts` asserts `fenceDocuments(ROOT)` equals
`snippetDocuments(ROOT)` — the fence guard re-implements the snippet gate's walk
so it needs NO install, and that pin is what makes a deliberate copy safe. The
previous commit widened the snippet gate onto the root `README.md` and left the
copy behind, so the pin went red on 223 vs 224 documents.
That is the pin doing exactly its job, so it is not touched: the fence guard's
surface is widened instead, and its header now STATES the surface (objectui#5174's
own finding, applied here as it was to the other two).
`ROOT_PAGES` cannot be shared by import — anything imported from
`check-doc-snippet-types.mjs` pulls in its `import ts from 'typescript'` at load,
and this guard's whole value is running install-free in the unfiltered workflow a
docs-only PR starts. So it stays a copy, on the same bargain the document walk and
`TS_FENCE_LANGUAGES` already make: copy freely, compare always. All three gates now
EXPORT `ROOT_PAGES` and a new pin compares all three, so the shared half cannot
drift even where the full surfaces legitimately differ (component-types does not
walk the package READMEs).
⭐ The root README needed no fence fix: with it in the walk the guard reports 225
documents and zero findings for it, and the green is not vacuous — un-fencing one
of its `tsx` blocks in a census probe produces `README.md:206 language:'' mode:
synonym`. The objectui#5867 shrink-only baseline is untouched: no entry added.
This makes FOUR gates the root README's blind spot reached, now measured rather
than assumed — the card's thesis proving itself a second time.
Part of #7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 01:06
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 75e7436Sep 3, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7115-root-readme-doc-gate-surface branch September 3, 2026 01:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationpackage: reactplugintests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card - #7419

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface
Sep 3, 2026
Merged

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card#7419
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7115

The root README.md — the repository's GitHub landing page and the npm page for the workspace, the most-read authored file in it — was outside the scan surface of every doc gate. check-doc-component-types walked content/docs. check-doc-snippet-types walked content/docs plus the package READMEs. The root README fell between the two, and taught stat-card four times in its flagship "dashboard in JSON" example. Nothing registers stat-card, so a reader who copied the headline snippet got four OBJUI-001 "Unknown component type" panels.

The ruling this implements

Maintainer, 2026-09-01, verbatim 「同意」 (comment 5494799307), quoted in the ruled order:

  1. 门禁盲区:根 README.md 加进 check-doc-component-typescheck-doc-snippet-types 的扫描面(最高价值半边,先落——内容修了不加面就会再烂);
  2. 内容修:四个 stat-card widget 改 statistic(真实注册且有 value 运送行,"${…}" 照常求值,示例保持表达力);
  3. 叉裁 B:非 text 类型不加运送行 —— 停止在无运送行的键上编写 ${…}(文档教现实);真实应用要哪个键求值 ⇒ 带消费者立 carriage-row 卡,届时按条款②扩

All three parts, one PR (lane's choice, as the ruling permits).

Part 1 — the gate blind spot, proved red-then-green

Widening a scan surface is exactly the change that can be green about nothing, so the widening was landed red first and the evidence is here rather than asserted.

check-doc-component-types (ROOT_PAGES = ['README.md'], joined to scanDocs's file list):

statefiles scannedexitwhat it said
before, unwidened1850green — the README was not in the walk
widened, content untouched18617 findings, 4 of them the ruled ones
widened + content fixed1860green

The failure that proves the walk reaches the file, verbatim from the run:

 README.md:290 [unregistered-doc-type] type 'stat-card' (json)
{ "type": "stat-card", "title": "Revenue", "value": "${stats.revenue}", "trend": "+12%", "w": 3, "h": 1 },
README.md:291 [unregistered-doc-type] type 'stat-card' (json)
README.md:292 [unregistered-doc-type] type 'stat-card' (json)
README.md:293 [unregistered-doc-type] type 'stat-card' (json)

Ablation, to show the pins can fail. With the implementation committed, the files.push(...ROOT_PAGES…) line was deleted (anchored grep 1 to 0, blob 6688e098 to d0441c22 — confirmed on disk before the run, not asserted). The gate went back to 185 files and printed ✅ Every documented component type is registered — the exact "green about nothing" state — and both new pins went red with the intended message. Restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and the restore is verified by state: git diff HEAD empty and the blob equal to the HEAD blob, not by an exit code.

check-doc-snippet-types — the root README does carry snippets its rules cover (five tsx/typescript blocks), so the same proof applies and is not hand-waved. Widening it took the scan set from 224 to 225 documents, and the build closure the gate computes for itself grew by exactly @object-ui/providers — a second, independent signal that the walk now reaches the file. It went red with 9 semantic diagnostics.

⚠️Read this half honestly: those diagnostics are declared debt, not fixed.README.md is now named in UNGATED_DOCS with a measured reason, which is that gate's own designed mechanism and the objectui#5174 distinction its header states — a document outside the walk is "neither covered NOR declared ungated", invisible to the gate's own accounting, whereas a ledgered one is named, counted, re-derived every run and shrink-only. Three of the nine are real defects (ObjectRenderer, registerDefaultRenderers, createObjectStackAdapter attributed to the wrong package) and are filed as #7417, with the reason string in the ledger naming that card. Making those blocks compile means resolving two phantom APIs, which is authoring work with an API question inside it — not this card.

The third gate — check-doc-fence-languages, found by its own coupling pin

The ruling named two gates. There are four.check-doc-fence-languages re-implements the snippet gate's walk (deliberately, so it needs no install — the snippet gate imports typescript, and an install-gated docs check is one a docs-only PR skips), and check-doc-fence-languages.test.ts asserts fenceDocuments(ROOT)equalssnippetDocuments(ROOT). Widening one surface without the other breaks that coupling by construction, and it did: the pin went red at 223 vs 224 documents.

The pin was not touched — it is doing precisely the job it was written for, and it is the only reason the third gate was found at all. The fence guard's surface is widened instead, and its header now states the surface where a reader will find it, as objectui#5174's own finding requires.

ROOT_PAGEScannot be shared by import: anything imported from check-doc-snippet-types.mjs pulls in its import ts from 'typescript' at load, which would install-gate the one gate whose value is running install-free. So it stays a copy, on the bargain the document walk and TS_FENCE_LANGUAGES already make — copy freely, compare always. All three now exportROOT_PAGES and a new pin compares all three, so the shared half cannot drift even where the full surfaces legitimately differ (component-types deliberately does not walk the package READMEs, so it cannot join the list-equality comparison).

The root README needed no fence fix, and that green is not vacuous. With it in the walk the guard reports 225 documents and zero findings for it; the objectui#5867 shrink-only baseline is untouched, with no entry added. Control, through the shipped census(): un-fencing one of the README's tsx blocks produces { rel: 'README.md', line: 206, language: '', mode: 'synonym' }, so the file is genuinely parsed and genuinely judged.

Four gates, measured rather than assumed

The card's thesis was that the most-read authored file in the repository was checked by nothing. The true number turned out to be larger than the ruling enumerated, which is the thesis proving itself a second time:

gatestatus
check-doc-component-typeswidened here
check-doc-snippet-typeswidened here
check-readme-exportssame blind spot, surface is packages/NAME/README.md only — filed as #7417
check-doc-fence-languageswidened here, found by its own coupling pin rather than by anyone looking

Part 2 — the content fix

The four widgets become statistic, registered at packages/components/src/renderers/data-display/statistic.tsx:64. ⭐ A retarget, not a downgrade to literals: measured from the built artifact (expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes), statistic carries [label, value, description], so "value": "${stats.revenue}" keeps evaluating.

Two keys moved with the type, because the renderer's real shape is not stat-card's: it reads schema.label (not title), and its trend is the enum up/down/neutral with the delta text in description. "trend": "+12%" would have rendered no arrow and no text — 文档教现实 applies to the retarget too.

Part 3 — fork ruled B, and no carriage row was added

Zero carriage rows added. The map lives in @objectstack/spec (a dependency), and this PR touches neither it nor SchemaRenderer.tsx.

Every ${…} rewritten, with the measured row set for its node type:

filenode typekeycarriage rows for that typenow
content/docs/guide/expressions.md (Percentage Bar)progressvaluenoneliteral 75, with visibleOn carrying the expression
content/docs/guide/expressions.md (Percentage Bar)progresslabelnoneliteral — same node, same defect, named on the card as :388
content/docs/guide/expressions.md (Computed Fields)inputvaluenoneshown by a text node's content, which is evaluated on every type
packages/plugin-dashboard/README.mdmetric-cardvaluenoneliteral, with a sentence pointing at the object-* widgets that do read live data
packages/plugin-dashboard/README.mdmetric-cardtrendnoneliteral 'up'
packages/react/README.mdinputvaluenonemoved to a sibling text node's content

On the card's own second-order note: it recorded, unmeasured, that metric-card and input "likely render the literal ${...}". That is now measured — both carry the empty set — but the rendering claim itself was never put in a browser and is not restated here as one.

⚠️ Deviations — flagged, not folded in silently

Widening a surface finds what the surface was hiding. Four things surfaced that the ruling's enumeration does not name, because the census behind it was taken before any gate had read this file. Each is stated so a reviewer can drop it on its own.

  1. crud × 2 — a RETIRED type. The widened gate found README.md:137 and :264 teaching crud. CRUDSchema and the crud node type were removed in objectui#5373 under ADR-0049; content/docs/api/schema-reference.md:541-:563 carries the retirement and an explicit "what to author instead" table, and validateSchema already returns RETIRED_TYPE for the name. The README was teaching a type the platform refuses by name. Retargeted per that table: the UserForm comparison onto object-form, the "Data Grid" example onto object-grid with that schema's real keys (field, not name, as its own doc row insists).
  2. submit — not a node type.README.md:256 authored "actions": [{ "type": "submit" }] under a form. FormSchema.actions is SchemaNode[], so this is a rendered node position, and ActionSchema.type is 'action' — the authentication.mdx exemption for submit does not reach here. The declared key for the built-in submit button is submitLabel, which is what it now says.
  3. card.value × 3 — ruling B, in a site the ruling's census never read. The "Basic Usage" snippet bound ${stats.*} in card.value; card carries [title, description], so value there is a literal. Retargeted onto statistic — the same move part 2 ordered, for the same reason.
  4. Findings left alone and filed rather than fixed: finding(docs): the root README's ts/tsx snippets do not compile — 3 names on no built dist/index.d.ts, incl. createObjectStackAdapter attributed to the wrong package #7417 (the root README's snippet debt, plus check-readme-exports' identical blind spot) and finding(docs): guide/expressions.md still authors ${…} in 6 more keys with no carriage row — badge.text / badge.variant / card.className / card.body, beyond the two the 2026-09-01 ruling named #7418 (six more non-carriage ${…} in guide/expressions.md beyond the two the ruling enumerated).

Items 1-3 are the card's own defect classes in the card's own file, and 1-2 are structurally forced: part 1 cannot land green while the file it now reads is red. They are bounded, and each correct form is pinned by evidence already in the tree — nothing here is a judgement call about an API.

Verification

All runs at 02efff4ba, the final commit, with a clean tree.

checkresult
check:doc-types✅ 186 files, 922 type literals, 787 registered
check:doc-snippets✅ 225 documents, 193 covered, 32 ungated, 417/417 blocks judged
check:doc-fences225 documents, root README contributing 0 findings (control fires — see above)
docs:check-links✅ 17 scan roots
check:readme-exports✅ 406 self-imports judged, 0 fabricated
check:control-bytes✅ 6109 files
check:pre-install-import-graph✅ 22 modules walked, every non-relative leaf a node builtin — the fence guard stays install-free
changeset:check + check-changeset-presence✅ — "no changeset is owed"; an empty-frontmatter declaration is here anyway, per the objectui#7114 precedent
the three gate suites + 7 derived tests✅ 10 files, 226 tests (the three gate suites carry +10 new pins)
eslint --no-inline-config over the changed lintable files✅ 0 errors, 0 warnings

On the eslint narrowing, stated so it reads as a measurement rather than a skip: ① the receiving population is read from eslint.config.js itself (files: ['**/*.{ts,tsx}'] plus narrower overrides; scripts/ is not in ignores) — the .mjs files are outside it and were passed explicitly, so this over-covers; ② the count comes from --format json: 0 errors, 0 warnings; ③ eslint.config.js has zero occurrences of projectService / parserOptions / project: (control: languageOptions matches once), so type-aware linting is off and no untouched file's verdict can depend on this diff.

check:eager-closure is unmoved, and this is structural rather than a byte reading. Locally the gate reports its own precondition — "No eager-closure report at apps/console/dist/eager-closure.json … a broken gauge, not a passing budget" — so it is NOT MEASURED here, not green. What is measured: 0 files in this diff are under apps/** or packages/*/src/**; the rest are scripts/ CI tooling and markdown. No source imports a README or a scripts/ file (the only ?raw imports in the tree are apps/console/index.html inside console tests; control: the same probe over d4c6a86a1 lists four real src/ files). Nothing here is a bundler input. CI confirms it: Bundle Analysis is green on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

…and stops teaching `stat-card`
The root `README.md` was outside the scan surface of EVERY doc gate.
`check-doc-component-types` walked `content/docs`; `check-doc-snippet-types`
walked `content/docs` plus the package READMEs; the most-read authored file in
the repository fell between the two. It taught `stat-card` four times, in the
flagship "dashboard in JSON" example, and `stat-card` is registered nowhere: a
reader who copied the headline snippet got four OBJUI-001 panels.
Maintainer ruling 2026-09-01 (verbatim 「同意」), in the ruled order:
1. The gate blind spot lands first — 「内容修了不加面就会再烂」.
2. The four widgets become `statistic`, which is registered
(`statistic.tsx:64`) and declares a `value` carriage row, so the example
keeps its expressions rather than degrading to literals.
3. The (A)/(B) fork is ruled B: non-`text` types get NO new carriage rows, and
the docs stop authoring `${…}` in keys that have none — 「文档教现实」.
Red-then-green, because a widened surface can be green about nothing: with
`ROOT_PAGES` added and the content untouched, `check-doc-component-types`
failed on `README.md:290`-`:293` naming `stat-card`; the content fix turned it
green. The same widening on `check-doc-snippet-types` red-flagged nine semantic
diagnostics, which are declared as measured debt in `UNGATED_DOCS` and filed as
objectui#7417 — the ledger names the file instead of the walk missing it.
Beyond the ruling's enumeration, and flagged rather than folded in silently:
`crud` (retired in objectui#5373) and `submit` were the other unregistered types
the widened gate found in the same file, and `card.value` carries no carriage
row. All four are the card's own defect classes in the card's own file; the PR
body names each with its evidence.
Fixes#7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation package: react plugin tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

…he third gate, found by its own coupling pin
`check-doc-fence-languages.test.ts` asserts `fenceDocuments(ROOT)` equals
`snippetDocuments(ROOT)` — the fence guard re-implements the snippet gate's walk
so it needs NO install, and that pin is what makes a deliberate copy safe. The
previous commit widened the snippet gate onto the root `README.md` and left the
copy behind, so the pin went red on 223 vs 224 documents.
That is the pin doing exactly its job, so it is not touched: the fence guard's
surface is widened instead, and its header now STATES the surface (objectui#5174's
own finding, applied here as it was to the other two).
`ROOT_PAGES` cannot be shared by import — anything imported from
`check-doc-snippet-types.mjs` pulls in its `import ts from 'typescript'` at load,
and this guard's whole value is running install-free in the unfiltered workflow a
docs-only PR starts. So it stays a copy, on the same bargain the document walk and
`TS_FENCE_LANGUAGES` already make: copy freely, compare always. All three gates now
EXPORT `ROOT_PAGES` and a new pin compares all three, so the shared half cannot
drift even where the full surfaces legitimately differ (component-types does not
walk the package READMEs).
⭐ The root README needed no fence fix: with it in the walk the guard reports 225
documents and zero findings for it, and the green is not vacuous — un-fencing one
of its `tsx` blocks in a census probe produces `README.md:206 language:'' mode:
synonym`. The objectui#5867 shrink-only baseline is untouched: no entry added.
This makes FOUR gates the root README's blind spot reached, now measured rather
than assumed — the card's thesis proving itself a second time.
Part of #7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 01:06
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 75e7436Sep 3, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7115-root-readme-doc-gate-surface branch September 3, 2026 01:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationpackage: reactplugintests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card - #7419

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface
Sep 3, 2026
Merged

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card#7419
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7115

The root README.md — the repository's GitHub landing page and the npm page for the workspace, the most-read authored file in it — was outside the scan surface of every doc gate. check-doc-component-types walked content/docs. check-doc-snippet-types walked content/docs plus the package READMEs. The root README fell between the two, and taught stat-card four times in its flagship "dashboard in JSON" example. Nothing registers stat-card, so a reader who copied the headline snippet got four OBJUI-001 "Unknown component type" panels.

The ruling this implements

Maintainer, 2026-09-01, verbatim 「同意」 (comment 5494799307), quoted in the ruled order:

  1. 门禁盲区:根 README.md 加进 check-doc-component-typescheck-doc-snippet-types 的扫描面(最高价值半边,先落——内容修了不加面就会再烂);
  2. 内容修:四个 stat-card widget 改 statistic(真实注册且有 value 运送行,"${…}" 照常求值,示例保持表达力);
  3. 叉裁 B:非 text 类型不加运送行 —— 停止在无运送行的键上编写 ${…}(文档教现实);真实应用要哪个键求值 ⇒ 带消费者立 carriage-row 卡,届时按条款②扩

All three parts, one PR (lane's choice, as the ruling permits).

Part 1 — the gate blind spot, proved red-then-green

Widening a scan surface is exactly the change that can be green about nothing, so the widening was landed red first and the evidence is here rather than asserted.

check-doc-component-types (ROOT_PAGES = ['README.md'], joined to scanDocs's file list):

statefiles scannedexitwhat it said
before, unwidened1850green — the README was not in the walk
widened, content untouched18617 findings, 4 of them the ruled ones
widened + content fixed1860green

The failure that proves the walk reaches the file, verbatim from the run:

 README.md:290 [unregistered-doc-type] type 'stat-card' (json)
{ "type": "stat-card", "title": "Revenue", "value": "${stats.revenue}", "trend": "+12%", "w": 3, "h": 1 },
README.md:291 [unregistered-doc-type] type 'stat-card' (json)
README.md:292 [unregistered-doc-type] type 'stat-card' (json)
README.md:293 [unregistered-doc-type] type 'stat-card' (json)

Ablation, to show the pins can fail. With the implementation committed, the files.push(...ROOT_PAGES…) line was deleted (anchored grep 1 to 0, blob 6688e098 to d0441c22 — confirmed on disk before the run, not asserted). The gate went back to 185 files and printed ✅ Every documented component type is registered — the exact "green about nothing" state — and both new pins went red with the intended message. Restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and the restore is verified by state: git diff HEAD empty and the blob equal to the HEAD blob, not by an exit code.

check-doc-snippet-types — the root README does carry snippets its rules cover (five tsx/typescript blocks), so the same proof applies and is not hand-waved. Widening it took the scan set from 224 to 225 documents, and the build closure the gate computes for itself grew by exactly @object-ui/providers — a second, independent signal that the walk now reaches the file. It went red with 9 semantic diagnostics.

⚠️Read this half honestly: those diagnostics are declared debt, not fixed.README.md is now named in UNGATED_DOCS with a measured reason, which is that gate's own designed mechanism and the objectui#5174 distinction its header states — a document outside the walk is "neither covered NOR declared ungated", invisible to the gate's own accounting, whereas a ledgered one is named, counted, re-derived every run and shrink-only. Three of the nine are real defects (ObjectRenderer, registerDefaultRenderers, createObjectStackAdapter attributed to the wrong package) and are filed as #7417, with the reason string in the ledger naming that card. Making those blocks compile means resolving two phantom APIs, which is authoring work with an API question inside it — not this card.

The third gate — check-doc-fence-languages, found by its own coupling pin

The ruling named two gates. There are four.check-doc-fence-languages re-implements the snippet gate's walk (deliberately, so it needs no install — the snippet gate imports typescript, and an install-gated docs check is one a docs-only PR skips), and check-doc-fence-languages.test.ts asserts fenceDocuments(ROOT)equalssnippetDocuments(ROOT). Widening one surface without the other breaks that coupling by construction, and it did: the pin went red at 223 vs 224 documents.

The pin was not touched — it is doing precisely the job it was written for, and it is the only reason the third gate was found at all. The fence guard's surface is widened instead, and its header now states the surface where a reader will find it, as objectui#5174's own finding requires.

ROOT_PAGEScannot be shared by import: anything imported from check-doc-snippet-types.mjs pulls in its import ts from 'typescript' at load, which would install-gate the one gate whose value is running install-free. So it stays a copy, on the bargain the document walk and TS_FENCE_LANGUAGES already make — copy freely, compare always. All three now exportROOT_PAGES and a new pin compares all three, so the shared half cannot drift even where the full surfaces legitimately differ (component-types deliberately does not walk the package READMEs, so it cannot join the list-equality comparison).

The root README needed no fence fix, and that green is not vacuous. With it in the walk the guard reports 225 documents and zero findings for it; the objectui#5867 shrink-only baseline is untouched, with no entry added. Control, through the shipped census(): un-fencing one of the README's tsx blocks produces { rel: 'README.md', line: 206, language: '', mode: 'synonym' }, so the file is genuinely parsed and genuinely judged.

Four gates, measured rather than assumed

The card's thesis was that the most-read authored file in the repository was checked by nothing. The true number turned out to be larger than the ruling enumerated, which is the thesis proving itself a second time:

gatestatus
check-doc-component-typeswidened here
check-doc-snippet-typeswidened here
check-readme-exportssame blind spot, surface is packages/NAME/README.md only — filed as #7417
check-doc-fence-languageswidened here, found by its own coupling pin rather than by anyone looking

Part 2 — the content fix

The four widgets become statistic, registered at packages/components/src/renderers/data-display/statistic.tsx:64. ⭐ A retarget, not a downgrade to literals: measured from the built artifact (expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes), statistic carries [label, value, description], so "value": "${stats.revenue}" keeps evaluating.

Two keys moved with the type, because the renderer's real shape is not stat-card's: it reads schema.label (not title), and its trend is the enum up/down/neutral with the delta text in description. "trend": "+12%" would have rendered no arrow and no text — 文档教现实 applies to the retarget too.

Part 3 — fork ruled B, and no carriage row was added

Zero carriage rows added. The map lives in @objectstack/spec (a dependency), and this PR touches neither it nor SchemaRenderer.tsx.

Every ${…} rewritten, with the measured row set for its node type:

filenode typekeycarriage rows for that typenow
content/docs/guide/expressions.md (Percentage Bar)progressvaluenoneliteral 75, with visibleOn carrying the expression
content/docs/guide/expressions.md (Percentage Bar)progresslabelnoneliteral — same node, same defect, named on the card as :388
content/docs/guide/expressions.md (Computed Fields)inputvaluenoneshown by a text node's content, which is evaluated on every type
packages/plugin-dashboard/README.mdmetric-cardvaluenoneliteral, with a sentence pointing at the object-* widgets that do read live data
packages/plugin-dashboard/README.mdmetric-cardtrendnoneliteral 'up'
packages/react/README.mdinputvaluenonemoved to a sibling text node's content

On the card's own second-order note: it recorded, unmeasured, that metric-card and input "likely render the literal ${...}". That is now measured — both carry the empty set — but the rendering claim itself was never put in a browser and is not restated here as one.

⚠️ Deviations — flagged, not folded in silently

Widening a surface finds what the surface was hiding. Four things surfaced that the ruling's enumeration does not name, because the census behind it was taken before any gate had read this file. Each is stated so a reviewer can drop it on its own.

  1. crud × 2 — a RETIRED type. The widened gate found README.md:137 and :264 teaching crud. CRUDSchema and the crud node type were removed in objectui#5373 under ADR-0049; content/docs/api/schema-reference.md:541-:563 carries the retirement and an explicit "what to author instead" table, and validateSchema already returns RETIRED_TYPE for the name. The README was teaching a type the platform refuses by name. Retargeted per that table: the UserForm comparison onto object-form, the "Data Grid" example onto object-grid with that schema's real keys (field, not name, as its own doc row insists).
  2. submit — not a node type.README.md:256 authored "actions": [{ "type": "submit" }] under a form. FormSchema.actions is SchemaNode[], so this is a rendered node position, and ActionSchema.type is 'action' — the authentication.mdx exemption for submit does not reach here. The declared key for the built-in submit button is submitLabel, which is what it now says.
  3. card.value × 3 — ruling B, in a site the ruling's census never read. The "Basic Usage" snippet bound ${stats.*} in card.value; card carries [title, description], so value there is a literal. Retargeted onto statistic — the same move part 2 ordered, for the same reason.
  4. Findings left alone and filed rather than fixed: finding(docs): the root README's ts/tsx snippets do not compile — 3 names on no built dist/index.d.ts, incl. createObjectStackAdapter attributed to the wrong package #7417 (the root README's snippet debt, plus check-readme-exports' identical blind spot) and finding(docs): guide/expressions.md still authors ${…} in 6 more keys with no carriage row — badge.text / badge.variant / card.className / card.body, beyond the two the 2026-09-01 ruling named #7418 (six more non-carriage ${…} in guide/expressions.md beyond the two the ruling enumerated).

Items 1-3 are the card's own defect classes in the card's own file, and 1-2 are structurally forced: part 1 cannot land green while the file it now reads is red. They are bounded, and each correct form is pinned by evidence already in the tree — nothing here is a judgement call about an API.

Verification

All runs at 02efff4ba, the final commit, with a clean tree.

checkresult
check:doc-types✅ 186 files, 922 type literals, 787 registered
check:doc-snippets✅ 225 documents, 193 covered, 32 ungated, 417/417 blocks judged
check:doc-fences225 documents, root README contributing 0 findings (control fires — see above)
docs:check-links✅ 17 scan roots
check:readme-exports✅ 406 self-imports judged, 0 fabricated
check:control-bytes✅ 6109 files
check:pre-install-import-graph✅ 22 modules walked, every non-relative leaf a node builtin — the fence guard stays install-free
changeset:check + check-changeset-presence✅ — "no changeset is owed"; an empty-frontmatter declaration is here anyway, per the objectui#7114 precedent
the three gate suites + 7 derived tests✅ 10 files, 226 tests (the three gate suites carry +10 new pins)
eslint --no-inline-config over the changed lintable files✅ 0 errors, 0 warnings

On the eslint narrowing, stated so it reads as a measurement rather than a skip: ① the receiving population is read from eslint.config.js itself (files: ['**/*.{ts,tsx}'] plus narrower overrides; scripts/ is not in ignores) — the .mjs files are outside it and were passed explicitly, so this over-covers; ② the count comes from --format json: 0 errors, 0 warnings; ③ eslint.config.js has zero occurrences of projectService / parserOptions / project: (control: languageOptions matches once), so type-aware linting is off and no untouched file's verdict can depend on this diff.

check:eager-closure is unmoved, and this is structural rather than a byte reading. Locally the gate reports its own precondition — "No eager-closure report at apps/console/dist/eager-closure.json … a broken gauge, not a passing budget" — so it is NOT MEASURED here, not green. What is measured: 0 files in this diff are under apps/** or packages/*/src/**; the rest are scripts/ CI tooling and markdown. No source imports a README or a scripts/ file (the only ?raw imports in the tree are apps/console/index.html inside console tests; control: the same probe over d4c6a86a1 lists four real src/ files). Nothing here is a bundler input. CI confirms it: Bundle Analysis is green on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

…and stops teaching `stat-card`
The root `README.md` was outside the scan surface of EVERY doc gate.
`check-doc-component-types` walked `content/docs`; `check-doc-snippet-types`
walked `content/docs` plus the package READMEs; the most-read authored file in
the repository fell between the two. It taught `stat-card` four times, in the
flagship "dashboard in JSON" example, and `stat-card` is registered nowhere: a
reader who copied the headline snippet got four OBJUI-001 panels.
Maintainer ruling 2026-09-01 (verbatim 「同意」), in the ruled order:
1. The gate blind spot lands first — 「内容修了不加面就会再烂」.
2. The four widgets become `statistic`, which is registered
(`statistic.tsx:64`) and declares a `value` carriage row, so the example
keeps its expressions rather than degrading to literals.
3. The (A)/(B) fork is ruled B: non-`text` types get NO new carriage rows, and
the docs stop authoring `${…}` in keys that have none — 「文档教现实」.
Red-then-green, because a widened surface can be green about nothing: with
`ROOT_PAGES` added and the content untouched, `check-doc-component-types`
failed on `README.md:290`-`:293` naming `stat-card`; the content fix turned it
green. The same widening on `check-doc-snippet-types` red-flagged nine semantic
diagnostics, which are declared as measured debt in `UNGATED_DOCS` and filed as
objectui#7417 — the ledger names the file instead of the walk missing it.
Beyond the ruling's enumeration, and flagged rather than folded in silently:
`crud` (retired in objectui#5373) and `submit` were the other unregistered types
the widened gate found in the same file, and `card.value` carries no carriage
row. All four are the card's own defect classes in the card's own file; the PR
body names each with its evidence.
Fixes#7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation package: react plugin tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

…he third gate, found by its own coupling pin
`check-doc-fence-languages.test.ts` asserts `fenceDocuments(ROOT)` equals
`snippetDocuments(ROOT)` — the fence guard re-implements the snippet gate's walk
so it needs NO install, and that pin is what makes a deliberate copy safe. The
previous commit widened the snippet gate onto the root `README.md` and left the
copy behind, so the pin went red on 223 vs 224 documents.
That is the pin doing exactly its job, so it is not touched: the fence guard's
surface is widened instead, and its header now STATES the surface (objectui#5174's
own finding, applied here as it was to the other two).
`ROOT_PAGES` cannot be shared by import — anything imported from
`check-doc-snippet-types.mjs` pulls in its `import ts from 'typescript'` at load,
and this guard's whole value is running install-free in the unfiltered workflow a
docs-only PR starts. So it stays a copy, on the same bargain the document walk and
`TS_FENCE_LANGUAGES` already make: copy freely, compare always. All three gates now
EXPORT `ROOT_PAGES` and a new pin compares all three, so the shared half cannot
drift even where the full surfaces legitimately differ (component-types does not
walk the package READMEs).
⭐ The root README needed no fence fix: with it in the walk the guard reports 225
documents and zero findings for it, and the green is not vacuous — un-fencing one
of its `tsx` blocks in a census probe produces `README.md:206 language:'' mode:
synonym`. The objectui#5867 shrink-only baseline is untouched: no entry added.
This makes FOUR gates the root README's blind spot reached, now measured rather
than assumed — the card's thesis proving itself a second time.
Part of #7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 01:06
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 75e7436Sep 3, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7115-root-readme-doc-gate-surface branch September 3, 2026 01:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationpackage: reactplugintests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card - #7419

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface
Sep 3, 2026
Merged

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card#7419
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7115

The root README.md — the repository's GitHub landing page and the npm page for the workspace, the most-read authored file in it — was outside the scan surface of every doc gate. check-doc-component-types walked content/docs. check-doc-snippet-types walked content/docs plus the package READMEs. The root README fell between the two, and taught stat-card four times in its flagship "dashboard in JSON" example. Nothing registers stat-card, so a reader who copied the headline snippet got four OBJUI-001 "Unknown component type" panels.

The ruling this implements

Maintainer, 2026-09-01, verbatim 「同意」 (comment 5494799307), quoted in the ruled order:

  1. 门禁盲区:根 README.md 加进 check-doc-component-typescheck-doc-snippet-types 的扫描面(最高价值半边,先落——内容修了不加面就会再烂);
  2. 内容修:四个 stat-card widget 改 statistic(真实注册且有 value 运送行,"${…}" 照常求值,示例保持表达力);
  3. 叉裁 B:非 text 类型不加运送行 —— 停止在无运送行的键上编写 ${…}(文档教现实);真实应用要哪个键求值 ⇒ 带消费者立 carriage-row 卡,届时按条款②扩

All three parts, one PR (lane's choice, as the ruling permits).

Part 1 — the gate blind spot, proved red-then-green

Widening a scan surface is exactly the change that can be green about nothing, so the widening was landed red first and the evidence is here rather than asserted.

check-doc-component-types (ROOT_PAGES = ['README.md'], joined to scanDocs's file list):

statefiles scannedexitwhat it said
before, unwidened1850green — the README was not in the walk
widened, content untouched18617 findings, 4 of them the ruled ones
widened + content fixed1860green

The failure that proves the walk reaches the file, verbatim from the run:

 README.md:290 [unregistered-doc-type] type 'stat-card' (json)
{ "type": "stat-card", "title": "Revenue", "value": "${stats.revenue}", "trend": "+12%", "w": 3, "h": 1 },
README.md:291 [unregistered-doc-type] type 'stat-card' (json)
README.md:292 [unregistered-doc-type] type 'stat-card' (json)
README.md:293 [unregistered-doc-type] type 'stat-card' (json)

Ablation, to show the pins can fail. With the implementation committed, the files.push(...ROOT_PAGES…) line was deleted (anchored grep 1 to 0, blob 6688e098 to d0441c22 — confirmed on disk before the run, not asserted). The gate went back to 185 files and printed ✅ Every documented component type is registered — the exact "green about nothing" state — and both new pins went red with the intended message. Restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and the restore is verified by state: git diff HEAD empty and the blob equal to the HEAD blob, not by an exit code.

check-doc-snippet-types — the root README does carry snippets its rules cover (five tsx/typescript blocks), so the same proof applies and is not hand-waved. Widening it took the scan set from 224 to 225 documents, and the build closure the gate computes for itself grew by exactly @object-ui/providers — a second, independent signal that the walk now reaches the file. It went red with 9 semantic diagnostics.

⚠️Read this half honestly: those diagnostics are declared debt, not fixed.README.md is now named in UNGATED_DOCS with a measured reason, which is that gate's own designed mechanism and the objectui#5174 distinction its header states — a document outside the walk is "neither covered NOR declared ungated", invisible to the gate's own accounting, whereas a ledgered one is named, counted, re-derived every run and shrink-only. Three of the nine are real defects (ObjectRenderer, registerDefaultRenderers, createObjectStackAdapter attributed to the wrong package) and are filed as #7417, with the reason string in the ledger naming that card. Making those blocks compile means resolving two phantom APIs, which is authoring work with an API question inside it — not this card.

The third gate — check-doc-fence-languages, found by its own coupling pin

The ruling named two gates. There are four.check-doc-fence-languages re-implements the snippet gate's walk (deliberately, so it needs no install — the snippet gate imports typescript, and an install-gated docs check is one a docs-only PR skips), and check-doc-fence-languages.test.ts asserts fenceDocuments(ROOT)equalssnippetDocuments(ROOT). Widening one surface without the other breaks that coupling by construction, and it did: the pin went red at 223 vs 224 documents.

The pin was not touched — it is doing precisely the job it was written for, and it is the only reason the third gate was found at all. The fence guard's surface is widened instead, and its header now states the surface where a reader will find it, as objectui#5174's own finding requires.

ROOT_PAGEScannot be shared by import: anything imported from check-doc-snippet-types.mjs pulls in its import ts from 'typescript' at load, which would install-gate the one gate whose value is running install-free. So it stays a copy, on the bargain the document walk and TS_FENCE_LANGUAGES already make — copy freely, compare always. All three now exportROOT_PAGES and a new pin compares all three, so the shared half cannot drift even where the full surfaces legitimately differ (component-types deliberately does not walk the package READMEs, so it cannot join the list-equality comparison).

The root README needed no fence fix, and that green is not vacuous. With it in the walk the guard reports 225 documents and zero findings for it; the objectui#5867 shrink-only baseline is untouched, with no entry added. Control, through the shipped census(): un-fencing one of the README's tsx blocks produces { rel: 'README.md', line: 206, language: '', mode: 'synonym' }, so the file is genuinely parsed and genuinely judged.

Four gates, measured rather than assumed

The card's thesis was that the most-read authored file in the repository was checked by nothing. The true number turned out to be larger than the ruling enumerated, which is the thesis proving itself a second time:

gatestatus
check-doc-component-typeswidened here
check-doc-snippet-typeswidened here
check-readme-exportssame blind spot, surface is packages/NAME/README.md only — filed as #7417
check-doc-fence-languageswidened here, found by its own coupling pin rather than by anyone looking

Part 2 — the content fix

The four widgets become statistic, registered at packages/components/src/renderers/data-display/statistic.tsx:64. ⭐ A retarget, not a downgrade to literals: measured from the built artifact (expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes), statistic carries [label, value, description], so "value": "${stats.revenue}" keeps evaluating.

Two keys moved with the type, because the renderer's real shape is not stat-card's: it reads schema.label (not title), and its trend is the enum up/down/neutral with the delta text in description. "trend": "+12%" would have rendered no arrow and no text — 文档教现实 applies to the retarget too.

Part 3 — fork ruled B, and no carriage row was added

Zero carriage rows added. The map lives in @objectstack/spec (a dependency), and this PR touches neither it nor SchemaRenderer.tsx.

Every ${…} rewritten, with the measured row set for its node type:

filenode typekeycarriage rows for that typenow
content/docs/guide/expressions.md (Percentage Bar)progressvaluenoneliteral 75, with visibleOn carrying the expression
content/docs/guide/expressions.md (Percentage Bar)progresslabelnoneliteral — same node, same defect, named on the card as :388
content/docs/guide/expressions.md (Computed Fields)inputvaluenoneshown by a text node's content, which is evaluated on every type
packages/plugin-dashboard/README.mdmetric-cardvaluenoneliteral, with a sentence pointing at the object-* widgets that do read live data
packages/plugin-dashboard/README.mdmetric-cardtrendnoneliteral 'up'
packages/react/README.mdinputvaluenonemoved to a sibling text node's content

On the card's own second-order note: it recorded, unmeasured, that metric-card and input "likely render the literal ${...}". That is now measured — both carry the empty set — but the rendering claim itself was never put in a browser and is not restated here as one.

⚠️ Deviations — flagged, not folded in silently

Widening a surface finds what the surface was hiding. Four things surfaced that the ruling's enumeration does not name, because the census behind it was taken before any gate had read this file. Each is stated so a reviewer can drop it on its own.

  1. crud × 2 — a RETIRED type. The widened gate found README.md:137 and :264 teaching crud. CRUDSchema and the crud node type were removed in objectui#5373 under ADR-0049; content/docs/api/schema-reference.md:541-:563 carries the retirement and an explicit "what to author instead" table, and validateSchema already returns RETIRED_TYPE for the name. The README was teaching a type the platform refuses by name. Retargeted per that table: the UserForm comparison onto object-form, the "Data Grid" example onto object-grid with that schema's real keys (field, not name, as its own doc row insists).
  2. submit — not a node type.README.md:256 authored "actions": [{ "type": "submit" }] under a form. FormSchema.actions is SchemaNode[], so this is a rendered node position, and ActionSchema.type is 'action' — the authentication.mdx exemption for submit does not reach here. The declared key for the built-in submit button is submitLabel, which is what it now says.
  3. card.value × 3 — ruling B, in a site the ruling's census never read. The "Basic Usage" snippet bound ${stats.*} in card.value; card carries [title, description], so value there is a literal. Retargeted onto statistic — the same move part 2 ordered, for the same reason.
  4. Findings left alone and filed rather than fixed: finding(docs): the root README's ts/tsx snippets do not compile — 3 names on no built dist/index.d.ts, incl. createObjectStackAdapter attributed to the wrong package #7417 (the root README's snippet debt, plus check-readme-exports' identical blind spot) and finding(docs): guide/expressions.md still authors ${…} in 6 more keys with no carriage row — badge.text / badge.variant / card.className / card.body, beyond the two the 2026-09-01 ruling named #7418 (six more non-carriage ${…} in guide/expressions.md beyond the two the ruling enumerated).

Items 1-3 are the card's own defect classes in the card's own file, and 1-2 are structurally forced: part 1 cannot land green while the file it now reads is red. They are bounded, and each correct form is pinned by evidence already in the tree — nothing here is a judgement call about an API.

Verification

All runs at 02efff4ba, the final commit, with a clean tree.

checkresult
check:doc-types✅ 186 files, 922 type literals, 787 registered
check:doc-snippets✅ 225 documents, 193 covered, 32 ungated, 417/417 blocks judged
check:doc-fences225 documents, root README contributing 0 findings (control fires — see above)
docs:check-links✅ 17 scan roots
check:readme-exports✅ 406 self-imports judged, 0 fabricated
check:control-bytes✅ 6109 files
check:pre-install-import-graph✅ 22 modules walked, every non-relative leaf a node builtin — the fence guard stays install-free
changeset:check + check-changeset-presence✅ — "no changeset is owed"; an empty-frontmatter declaration is here anyway, per the objectui#7114 precedent
the three gate suites + 7 derived tests✅ 10 files, 226 tests (the three gate suites carry +10 new pins)
eslint --no-inline-config over the changed lintable files✅ 0 errors, 0 warnings

On the eslint narrowing, stated so it reads as a measurement rather than a skip: ① the receiving population is read from eslint.config.js itself (files: ['**/*.{ts,tsx}'] plus narrower overrides; scripts/ is not in ignores) — the .mjs files are outside it and were passed explicitly, so this over-covers; ② the count comes from --format json: 0 errors, 0 warnings; ③ eslint.config.js has zero occurrences of projectService / parserOptions / project: (control: languageOptions matches once), so type-aware linting is off and no untouched file's verdict can depend on this diff.

check:eager-closure is unmoved, and this is structural rather than a byte reading. Locally the gate reports its own precondition — "No eager-closure report at apps/console/dist/eager-closure.json … a broken gauge, not a passing budget" — so it is NOT MEASURED here, not green. What is measured: 0 files in this diff are under apps/** or packages/*/src/**; the rest are scripts/ CI tooling and markdown. No source imports a README or a scripts/ file (the only ?raw imports in the tree are apps/console/index.html inside console tests; control: the same probe over d4c6a86a1 lists four real src/ files). Nothing here is a bundler input. CI confirms it: Bundle Analysis is green on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

…and stops teaching `stat-card`
The root `README.md` was outside the scan surface of EVERY doc gate.
`check-doc-component-types` walked `content/docs`; `check-doc-snippet-types`
walked `content/docs` plus the package READMEs; the most-read authored file in
the repository fell between the two. It taught `stat-card` four times, in the
flagship "dashboard in JSON" example, and `stat-card` is registered nowhere: a
reader who copied the headline snippet got four OBJUI-001 panels.
Maintainer ruling 2026-09-01 (verbatim 「同意」), in the ruled order:
1. The gate blind spot lands first — 「内容修了不加面就会再烂」.
2. The four widgets become `statistic`, which is registered
(`statistic.tsx:64`) and declares a `value` carriage row, so the example
keeps its expressions rather than degrading to literals.
3. The (A)/(B) fork is ruled B: non-`text` types get NO new carriage rows, and
the docs stop authoring `${…}` in keys that have none — 「文档教现实」.
Red-then-green, because a widened surface can be green about nothing: with
`ROOT_PAGES` added and the content untouched, `check-doc-component-types`
failed on `README.md:290`-`:293` naming `stat-card`; the content fix turned it
green. The same widening on `check-doc-snippet-types` red-flagged nine semantic
diagnostics, which are declared as measured debt in `UNGATED_DOCS` and filed as
objectui#7417 — the ledger names the file instead of the walk missing it.
Beyond the ruling's enumeration, and flagged rather than folded in silently:
`crud` (retired in objectui#5373) and `submit` were the other unregistered types
the widened gate found in the same file, and `card.value` carries no carriage
row. All four are the card's own defect classes in the card's own file; the PR
body names each with its evidence.
Fixes#7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation package: react plugin tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

…he third gate, found by its own coupling pin
`check-doc-fence-languages.test.ts` asserts `fenceDocuments(ROOT)` equals
`snippetDocuments(ROOT)` — the fence guard re-implements the snippet gate's walk
so it needs NO install, and that pin is what makes a deliberate copy safe. The
previous commit widened the snippet gate onto the root `README.md` and left the
copy behind, so the pin went red on 223 vs 224 documents.
That is the pin doing exactly its job, so it is not touched: the fence guard's
surface is widened instead, and its header now STATES the surface (objectui#5174's
own finding, applied here as it was to the other two).
`ROOT_PAGES` cannot be shared by import — anything imported from
`check-doc-snippet-types.mjs` pulls in its `import ts from 'typescript'` at load,
and this guard's whole value is running install-free in the unfiltered workflow a
docs-only PR starts. So it stays a copy, on the same bargain the document walk and
`TS_FENCE_LANGUAGES` already make: copy freely, compare always. All three gates now
EXPORT `ROOT_PAGES` and a new pin compares all three, so the shared half cannot
drift even where the full surfaces legitimately differ (component-types does not
walk the package READMEs).
⭐ The root README needed no fence fix: with it in the walk the guard reports 225
documents and zero findings for it, and the green is not vacuous — un-fencing one
of its `tsx` blocks in a census probe produces `README.md:206 language:'' mode:
synonym`. The objectui#5867 shrink-only baseline is untouched: no entry added.
This makes FOUR gates the root README's blind spot reached, now measured rather
than assumed — the card's thesis proving itself a second time.
Part of #7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 01:06
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 75e7436Sep 3, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7115-root-readme-doc-gate-surface branch September 3, 2026 01:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationpackage: reactplugintests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card - #7419

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface
Sep 3, 2026
Merged

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card#7419
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7115

The root README.md — the repository's GitHub landing page and the npm page for the workspace, the most-read authored file in it — was outside the scan surface of every doc gate. check-doc-component-types walked content/docs. check-doc-snippet-types walked content/docs plus the package READMEs. The root README fell between the two, and taught stat-card four times in its flagship "dashboard in JSON" example. Nothing registers stat-card, so a reader who copied the headline snippet got four OBJUI-001 "Unknown component type" panels.

The ruling this implements

Maintainer, 2026-09-01, verbatim 「同意」 (comment 5494799307), quoted in the ruled order:

  1. 门禁盲区:根 README.md 加进 check-doc-component-typescheck-doc-snippet-types 的扫描面(最高价值半边,先落——内容修了不加面就会再烂);
  2. 内容修:四个 stat-card widget 改 statistic(真实注册且有 value 运送行,"${…}" 照常求值,示例保持表达力);
  3. 叉裁 B:非 text 类型不加运送行 —— 停止在无运送行的键上编写 ${…}(文档教现实);真实应用要哪个键求值 ⇒ 带消费者立 carriage-row 卡,届时按条款②扩

All three parts, one PR (lane's choice, as the ruling permits).

Part 1 — the gate blind spot, proved red-then-green

Widening a scan surface is exactly the change that can be green about nothing, so the widening was landed red first and the evidence is here rather than asserted.

check-doc-component-types (ROOT_PAGES = ['README.md'], joined to scanDocs's file list):

statefiles scannedexitwhat it said
before, unwidened1850green — the README was not in the walk
widened, content untouched18617 findings, 4 of them the ruled ones
widened + content fixed1860green

The failure that proves the walk reaches the file, verbatim from the run:

 README.md:290 [unregistered-doc-type] type 'stat-card' (json)
{ "type": "stat-card", "title": "Revenue", "value": "${stats.revenue}", "trend": "+12%", "w": 3, "h": 1 },
README.md:291 [unregistered-doc-type] type 'stat-card' (json)
README.md:292 [unregistered-doc-type] type 'stat-card' (json)
README.md:293 [unregistered-doc-type] type 'stat-card' (json)

Ablation, to show the pins can fail. With the implementation committed, the files.push(...ROOT_PAGES…) line was deleted (anchored grep 1 to 0, blob 6688e098 to d0441c22 — confirmed on disk before the run, not asserted). The gate went back to 185 files and printed ✅ Every documented component type is registered — the exact "green about nothing" state — and both new pins went red with the intended message. Restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and the restore is verified by state: git diff HEAD empty and the blob equal to the HEAD blob, not by an exit code.

check-doc-snippet-types — the root README does carry snippets its rules cover (five tsx/typescript blocks), so the same proof applies and is not hand-waved. Widening it took the scan set from 224 to 225 documents, and the build closure the gate computes for itself grew by exactly @object-ui/providers — a second, independent signal that the walk now reaches the file. It went red with 9 semantic diagnostics.

⚠️Read this half honestly: those diagnostics are declared debt, not fixed.README.md is now named in UNGATED_DOCS with a measured reason, which is that gate's own designed mechanism and the objectui#5174 distinction its header states — a document outside the walk is "neither covered NOR declared ungated", invisible to the gate's own accounting, whereas a ledgered one is named, counted, re-derived every run and shrink-only. Three of the nine are real defects (ObjectRenderer, registerDefaultRenderers, createObjectStackAdapter attributed to the wrong package) and are filed as #7417, with the reason string in the ledger naming that card. Making those blocks compile means resolving two phantom APIs, which is authoring work with an API question inside it — not this card.

The third gate — check-doc-fence-languages, found by its own coupling pin

The ruling named two gates. There are four.check-doc-fence-languages re-implements the snippet gate's walk (deliberately, so it needs no install — the snippet gate imports typescript, and an install-gated docs check is one a docs-only PR skips), and check-doc-fence-languages.test.ts asserts fenceDocuments(ROOT)equalssnippetDocuments(ROOT). Widening one surface without the other breaks that coupling by construction, and it did: the pin went red at 223 vs 224 documents.

The pin was not touched — it is doing precisely the job it was written for, and it is the only reason the third gate was found at all. The fence guard's surface is widened instead, and its header now states the surface where a reader will find it, as objectui#5174's own finding requires.

ROOT_PAGEScannot be shared by import: anything imported from check-doc-snippet-types.mjs pulls in its import ts from 'typescript' at load, which would install-gate the one gate whose value is running install-free. So it stays a copy, on the bargain the document walk and TS_FENCE_LANGUAGES already make — copy freely, compare always. All three now exportROOT_PAGES and a new pin compares all three, so the shared half cannot drift even where the full surfaces legitimately differ (component-types deliberately does not walk the package READMEs, so it cannot join the list-equality comparison).

The root README needed no fence fix, and that green is not vacuous. With it in the walk the guard reports 225 documents and zero findings for it; the objectui#5867 shrink-only baseline is untouched, with no entry added. Control, through the shipped census(): un-fencing one of the README's tsx blocks produces { rel: 'README.md', line: 206, language: '', mode: 'synonym' }, so the file is genuinely parsed and genuinely judged.

Four gates, measured rather than assumed

The card's thesis was that the most-read authored file in the repository was checked by nothing. The true number turned out to be larger than the ruling enumerated, which is the thesis proving itself a second time:

gatestatus
check-doc-component-typeswidened here
check-doc-snippet-typeswidened here
check-readme-exportssame blind spot, surface is packages/NAME/README.md only — filed as #7417
check-doc-fence-languageswidened here, found by its own coupling pin rather than by anyone looking

Part 2 — the content fix

The four widgets become statistic, registered at packages/components/src/renderers/data-display/statistic.tsx:64. ⭐ A retarget, not a downgrade to literals: measured from the built artifact (expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes), statistic carries [label, value, description], so "value": "${stats.revenue}" keeps evaluating.

Two keys moved with the type, because the renderer's real shape is not stat-card's: it reads schema.label (not title), and its trend is the enum up/down/neutral with the delta text in description. "trend": "+12%" would have rendered no arrow and no text — 文档教现实 applies to the retarget too.

Part 3 — fork ruled B, and no carriage row was added

Zero carriage rows added. The map lives in @objectstack/spec (a dependency), and this PR touches neither it nor SchemaRenderer.tsx.

Every ${…} rewritten, with the measured row set for its node type:

filenode typekeycarriage rows for that typenow
content/docs/guide/expressions.md (Percentage Bar)progressvaluenoneliteral 75, with visibleOn carrying the expression
content/docs/guide/expressions.md (Percentage Bar)progresslabelnoneliteral — same node, same defect, named on the card as :388
content/docs/guide/expressions.md (Computed Fields)inputvaluenoneshown by a text node's content, which is evaluated on every type
packages/plugin-dashboard/README.mdmetric-cardvaluenoneliteral, with a sentence pointing at the object-* widgets that do read live data
packages/plugin-dashboard/README.mdmetric-cardtrendnoneliteral 'up'
packages/react/README.mdinputvaluenonemoved to a sibling text node's content

On the card's own second-order note: it recorded, unmeasured, that metric-card and input "likely render the literal ${...}". That is now measured — both carry the empty set — but the rendering claim itself was never put in a browser and is not restated here as one.

⚠️ Deviations — flagged, not folded in silently

Widening a surface finds what the surface was hiding. Four things surfaced that the ruling's enumeration does not name, because the census behind it was taken before any gate had read this file. Each is stated so a reviewer can drop it on its own.

  1. crud × 2 — a RETIRED type. The widened gate found README.md:137 and :264 teaching crud. CRUDSchema and the crud node type were removed in objectui#5373 under ADR-0049; content/docs/api/schema-reference.md:541-:563 carries the retirement and an explicit "what to author instead" table, and validateSchema already returns RETIRED_TYPE for the name. The README was teaching a type the platform refuses by name. Retargeted per that table: the UserForm comparison onto object-form, the "Data Grid" example onto object-grid with that schema's real keys (field, not name, as its own doc row insists).
  2. submit — not a node type.README.md:256 authored "actions": [{ "type": "submit" }] under a form. FormSchema.actions is SchemaNode[], so this is a rendered node position, and ActionSchema.type is 'action' — the authentication.mdx exemption for submit does not reach here. The declared key for the built-in submit button is submitLabel, which is what it now says.
  3. card.value × 3 — ruling B, in a site the ruling's census never read. The "Basic Usage" snippet bound ${stats.*} in card.value; card carries [title, description], so value there is a literal. Retargeted onto statistic — the same move part 2 ordered, for the same reason.
  4. Findings left alone and filed rather than fixed: finding(docs): the root README's ts/tsx snippets do not compile — 3 names on no built dist/index.d.ts, incl. createObjectStackAdapter attributed to the wrong package #7417 (the root README's snippet debt, plus check-readme-exports' identical blind spot) and finding(docs): guide/expressions.md still authors ${…} in 6 more keys with no carriage row — badge.text / badge.variant / card.className / card.body, beyond the two the 2026-09-01 ruling named #7418 (six more non-carriage ${…} in guide/expressions.md beyond the two the ruling enumerated).

Items 1-3 are the card's own defect classes in the card's own file, and 1-2 are structurally forced: part 1 cannot land green while the file it now reads is red. They are bounded, and each correct form is pinned by evidence already in the tree — nothing here is a judgement call about an API.

Verification

All runs at 02efff4ba, the final commit, with a clean tree.

checkresult
check:doc-types✅ 186 files, 922 type literals, 787 registered
check:doc-snippets✅ 225 documents, 193 covered, 32 ungated, 417/417 blocks judged
check:doc-fences225 documents, root README contributing 0 findings (control fires — see above)
docs:check-links✅ 17 scan roots
check:readme-exports✅ 406 self-imports judged, 0 fabricated
check:control-bytes✅ 6109 files
check:pre-install-import-graph✅ 22 modules walked, every non-relative leaf a node builtin — the fence guard stays install-free
changeset:check + check-changeset-presence✅ — "no changeset is owed"; an empty-frontmatter declaration is here anyway, per the objectui#7114 precedent
the three gate suites + 7 derived tests✅ 10 files, 226 tests (the three gate suites carry +10 new pins)
eslint --no-inline-config over the changed lintable files✅ 0 errors, 0 warnings

On the eslint narrowing, stated so it reads as a measurement rather than a skip: ① the receiving population is read from eslint.config.js itself (files: ['**/*.{ts,tsx}'] plus narrower overrides; scripts/ is not in ignores) — the .mjs files are outside it and were passed explicitly, so this over-covers; ② the count comes from --format json: 0 errors, 0 warnings; ③ eslint.config.js has zero occurrences of projectService / parserOptions / project: (control: languageOptions matches once), so type-aware linting is off and no untouched file's verdict can depend on this diff.

check:eager-closure is unmoved, and this is structural rather than a byte reading. Locally the gate reports its own precondition — "No eager-closure report at apps/console/dist/eager-closure.json … a broken gauge, not a passing budget" — so it is NOT MEASURED here, not green. What is measured: 0 files in this diff are under apps/** or packages/*/src/**; the rest are scripts/ CI tooling and markdown. No source imports a README or a scripts/ file (the only ?raw imports in the tree are apps/console/index.html inside console tests; control: the same probe over d4c6a86a1 lists four real src/ files). Nothing here is a bundler input. CI confirms it: Bundle Analysis is green on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

…and stops teaching `stat-card`
The root `README.md` was outside the scan surface of EVERY doc gate.
`check-doc-component-types` walked `content/docs`; `check-doc-snippet-types`
walked `content/docs` plus the package READMEs; the most-read authored file in
the repository fell between the two. It taught `stat-card` four times, in the
flagship "dashboard in JSON" example, and `stat-card` is registered nowhere: a
reader who copied the headline snippet got four OBJUI-001 panels.
Maintainer ruling 2026-09-01 (verbatim 「同意」), in the ruled order:
1. The gate blind spot lands first — 「内容修了不加面就会再烂」.
2. The four widgets become `statistic`, which is registered
(`statistic.tsx:64`) and declares a `value` carriage row, so the example
keeps its expressions rather than degrading to literals.
3. The (A)/(B) fork is ruled B: non-`text` types get NO new carriage rows, and
the docs stop authoring `${…}` in keys that have none — 「文档教现实」.
Red-then-green, because a widened surface can be green about nothing: with
`ROOT_PAGES` added and the content untouched, `check-doc-component-types`
failed on `README.md:290`-`:293` naming `stat-card`; the content fix turned it
green. The same widening on `check-doc-snippet-types` red-flagged nine semantic
diagnostics, which are declared as measured debt in `UNGATED_DOCS` and filed as
objectui#7417 — the ledger names the file instead of the walk missing it.
Beyond the ruling's enumeration, and flagged rather than folded in silently:
`crud` (retired in objectui#5373) and `submit` were the other unregistered types
the widened gate found in the same file, and `card.value` carries no carriage
row. All four are the card's own defect classes in the card's own file; the PR
body names each with its evidence.
Fixes#7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation package: react plugin tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

…he third gate, found by its own coupling pin
`check-doc-fence-languages.test.ts` asserts `fenceDocuments(ROOT)` equals
`snippetDocuments(ROOT)` — the fence guard re-implements the snippet gate's walk
so it needs NO install, and that pin is what makes a deliberate copy safe. The
previous commit widened the snippet gate onto the root `README.md` and left the
copy behind, so the pin went red on 223 vs 224 documents.
That is the pin doing exactly its job, so it is not touched: the fence guard's
surface is widened instead, and its header now STATES the surface (objectui#5174's
own finding, applied here as it was to the other two).
`ROOT_PAGES` cannot be shared by import — anything imported from
`check-doc-snippet-types.mjs` pulls in its `import ts from 'typescript'` at load,
and this guard's whole value is running install-free in the unfiltered workflow a
docs-only PR starts. So it stays a copy, on the same bargain the document walk and
`TS_FENCE_LANGUAGES` already make: copy freely, compare always. All three gates now
EXPORT `ROOT_PAGES` and a new pin compares all three, so the shared half cannot
drift even where the full surfaces legitimately differ (component-types does not
walk the package READMEs).
⭐ The root README needed no fence fix: with it in the walk the guard reports 225
documents and zero findings for it, and the green is not vacuous — un-fencing one
of its `tsx` blocks in a census probe produces `README.md:206 language:'' mode:
synonym`. The objectui#5867 shrink-only baseline is untouched: no entry added.
This makes FOUR gates the root README's blind spot reached, now measured rather
than assumed — the card's thesis proving itself a second time.
Part of #7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 01:06
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 75e7436Sep 3, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7115-root-readme-doc-gate-surface branch September 3, 2026 01:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationpackage: reactplugintests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card - #7419

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface
Sep 3, 2026
Merged

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card#7419
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7115

The root README.md — the repository's GitHub landing page and the npm page for the workspace, the most-read authored file in it — was outside the scan surface of every doc gate. check-doc-component-types walked content/docs. check-doc-snippet-types walked content/docs plus the package READMEs. The root README fell between the two, and taught stat-card four times in its flagship "dashboard in JSON" example. Nothing registers stat-card, so a reader who copied the headline snippet got four OBJUI-001 "Unknown component type" panels.

The ruling this implements

Maintainer, 2026-09-01, verbatim 「同意」 (comment 5494799307), quoted in the ruled order:

  1. 门禁盲区:根 README.md 加进 check-doc-component-typescheck-doc-snippet-types 的扫描面(最高价值半边,先落——内容修了不加面就会再烂);
  2. 内容修:四个 stat-card widget 改 statistic(真实注册且有 value 运送行,"${…}" 照常求值,示例保持表达力);
  3. 叉裁 B:非 text 类型不加运送行 —— 停止在无运送行的键上编写 ${…}(文档教现实);真实应用要哪个键求值 ⇒ 带消费者立 carriage-row 卡,届时按条款②扩

All three parts, one PR (lane's choice, as the ruling permits).

Part 1 — the gate blind spot, proved red-then-green

Widening a scan surface is exactly the change that can be green about nothing, so the widening was landed red first and the evidence is here rather than asserted.

check-doc-component-types (ROOT_PAGES = ['README.md'], joined to scanDocs's file list):

statefiles scannedexitwhat it said
before, unwidened1850green — the README was not in the walk
widened, content untouched18617 findings, 4 of them the ruled ones
widened + content fixed1860green

The failure that proves the walk reaches the file, verbatim from the run:

 README.md:290 [unregistered-doc-type] type 'stat-card' (json)
{ "type": "stat-card", "title": "Revenue", "value": "${stats.revenue}", "trend": "+12%", "w": 3, "h": 1 },
README.md:291 [unregistered-doc-type] type 'stat-card' (json)
README.md:292 [unregistered-doc-type] type 'stat-card' (json)
README.md:293 [unregistered-doc-type] type 'stat-card' (json)

Ablation, to show the pins can fail. With the implementation committed, the files.push(...ROOT_PAGES…) line was deleted (anchored grep 1 to 0, blob 6688e098 to d0441c22 — confirmed on disk before the run, not asserted). The gate went back to 185 files and printed ✅ Every documented component type is registered — the exact "green about nothing" state — and both new pins went red with the intended message. Restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and the restore is verified by state: git diff HEAD empty and the blob equal to the HEAD blob, not by an exit code.

check-doc-snippet-types — the root README does carry snippets its rules cover (five tsx/typescript blocks), so the same proof applies and is not hand-waved. Widening it took the scan set from 224 to 225 documents, and the build closure the gate computes for itself grew by exactly @object-ui/providers — a second, independent signal that the walk now reaches the file. It went red with 9 semantic diagnostics.

⚠️Read this half honestly: those diagnostics are declared debt, not fixed.README.md is now named in UNGATED_DOCS with a measured reason, which is that gate's own designed mechanism and the objectui#5174 distinction its header states — a document outside the walk is "neither covered NOR declared ungated", invisible to the gate's own accounting, whereas a ledgered one is named, counted, re-derived every run and shrink-only. Three of the nine are real defects (ObjectRenderer, registerDefaultRenderers, createObjectStackAdapter attributed to the wrong package) and are filed as #7417, with the reason string in the ledger naming that card. Making those blocks compile means resolving two phantom APIs, which is authoring work with an API question inside it — not this card.

The third gate — check-doc-fence-languages, found by its own coupling pin

The ruling named two gates. There are four.check-doc-fence-languages re-implements the snippet gate's walk (deliberately, so it needs no install — the snippet gate imports typescript, and an install-gated docs check is one a docs-only PR skips), and check-doc-fence-languages.test.ts asserts fenceDocuments(ROOT)equalssnippetDocuments(ROOT). Widening one surface without the other breaks that coupling by construction, and it did: the pin went red at 223 vs 224 documents.

The pin was not touched — it is doing precisely the job it was written for, and it is the only reason the third gate was found at all. The fence guard's surface is widened instead, and its header now states the surface where a reader will find it, as objectui#5174's own finding requires.

ROOT_PAGEScannot be shared by import: anything imported from check-doc-snippet-types.mjs pulls in its import ts from 'typescript' at load, which would install-gate the one gate whose value is running install-free. So it stays a copy, on the bargain the document walk and TS_FENCE_LANGUAGES already make — copy freely, compare always. All three now exportROOT_PAGES and a new pin compares all three, so the shared half cannot drift even where the full surfaces legitimately differ (component-types deliberately does not walk the package READMEs, so it cannot join the list-equality comparison).

The root README needed no fence fix, and that green is not vacuous. With it in the walk the guard reports 225 documents and zero findings for it; the objectui#5867 shrink-only baseline is untouched, with no entry added. Control, through the shipped census(): un-fencing one of the README's tsx blocks produces { rel: 'README.md', line: 206, language: '', mode: 'synonym' }, so the file is genuinely parsed and genuinely judged.

Four gates, measured rather than assumed

The card's thesis was that the most-read authored file in the repository was checked by nothing. The true number turned out to be larger than the ruling enumerated, which is the thesis proving itself a second time:

gatestatus
check-doc-component-typeswidened here
check-doc-snippet-typeswidened here
check-readme-exportssame blind spot, surface is packages/NAME/README.md only — filed as #7417
check-doc-fence-languageswidened here, found by its own coupling pin rather than by anyone looking

Part 2 — the content fix

The four widgets become statistic, registered at packages/components/src/renderers/data-display/statistic.tsx:64. ⭐ A retarget, not a downgrade to literals: measured from the built artifact (expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes), statistic carries [label, value, description], so "value": "${stats.revenue}" keeps evaluating.

Two keys moved with the type, because the renderer's real shape is not stat-card's: it reads schema.label (not title), and its trend is the enum up/down/neutral with the delta text in description. "trend": "+12%" would have rendered no arrow and no text — 文档教现实 applies to the retarget too.

Part 3 — fork ruled B, and no carriage row was added

Zero carriage rows added. The map lives in @objectstack/spec (a dependency), and this PR touches neither it nor SchemaRenderer.tsx.

Every ${…} rewritten, with the measured row set for its node type:

filenode typekeycarriage rows for that typenow
content/docs/guide/expressions.md (Percentage Bar)progressvaluenoneliteral 75, with visibleOn carrying the expression
content/docs/guide/expressions.md (Percentage Bar)progresslabelnoneliteral — same node, same defect, named on the card as :388
content/docs/guide/expressions.md (Computed Fields)inputvaluenoneshown by a text node's content, which is evaluated on every type
packages/plugin-dashboard/README.mdmetric-cardvaluenoneliteral, with a sentence pointing at the object-* widgets that do read live data
packages/plugin-dashboard/README.mdmetric-cardtrendnoneliteral 'up'
packages/react/README.mdinputvaluenonemoved to a sibling text node's content

On the card's own second-order note: it recorded, unmeasured, that metric-card and input "likely render the literal ${...}". That is now measured — both carry the empty set — but the rendering claim itself was never put in a browser and is not restated here as one.

⚠️ Deviations — flagged, not folded in silently

Widening a surface finds what the surface was hiding. Four things surfaced that the ruling's enumeration does not name, because the census behind it was taken before any gate had read this file. Each is stated so a reviewer can drop it on its own.

  1. crud × 2 — a RETIRED type. The widened gate found README.md:137 and :264 teaching crud. CRUDSchema and the crud node type were removed in objectui#5373 under ADR-0049; content/docs/api/schema-reference.md:541-:563 carries the retirement and an explicit "what to author instead" table, and validateSchema already returns RETIRED_TYPE for the name. The README was teaching a type the platform refuses by name. Retargeted per that table: the UserForm comparison onto object-form, the "Data Grid" example onto object-grid with that schema's real keys (field, not name, as its own doc row insists).
  2. submit — not a node type.README.md:256 authored "actions": [{ "type": "submit" }] under a form. FormSchema.actions is SchemaNode[], so this is a rendered node position, and ActionSchema.type is 'action' — the authentication.mdx exemption for submit does not reach here. The declared key for the built-in submit button is submitLabel, which is what it now says.
  3. card.value × 3 — ruling B, in a site the ruling's census never read. The "Basic Usage" snippet bound ${stats.*} in card.value; card carries [title, description], so value there is a literal. Retargeted onto statistic — the same move part 2 ordered, for the same reason.
  4. Findings left alone and filed rather than fixed: finding(docs): the root README's ts/tsx snippets do not compile — 3 names on no built dist/index.d.ts, incl. createObjectStackAdapter attributed to the wrong package #7417 (the root README's snippet debt, plus check-readme-exports' identical blind spot) and finding(docs): guide/expressions.md still authors ${…} in 6 more keys with no carriage row — badge.text / badge.variant / card.className / card.body, beyond the two the 2026-09-01 ruling named #7418 (six more non-carriage ${…} in guide/expressions.md beyond the two the ruling enumerated).

Items 1-3 are the card's own defect classes in the card's own file, and 1-2 are structurally forced: part 1 cannot land green while the file it now reads is red. They are bounded, and each correct form is pinned by evidence already in the tree — nothing here is a judgement call about an API.

Verification

All runs at 02efff4ba, the final commit, with a clean tree.

checkresult
check:doc-types✅ 186 files, 922 type literals, 787 registered
check:doc-snippets✅ 225 documents, 193 covered, 32 ungated, 417/417 blocks judged
check:doc-fences225 documents, root README contributing 0 findings (control fires — see above)
docs:check-links✅ 17 scan roots
check:readme-exports✅ 406 self-imports judged, 0 fabricated
check:control-bytes✅ 6109 files
check:pre-install-import-graph✅ 22 modules walked, every non-relative leaf a node builtin — the fence guard stays install-free
changeset:check + check-changeset-presence✅ — "no changeset is owed"; an empty-frontmatter declaration is here anyway, per the objectui#7114 precedent
the three gate suites + 7 derived tests✅ 10 files, 226 tests (the three gate suites carry +10 new pins)
eslint --no-inline-config over the changed lintable files✅ 0 errors, 0 warnings

On the eslint narrowing, stated so it reads as a measurement rather than a skip: ① the receiving population is read from eslint.config.js itself (files: ['**/*.{ts,tsx}'] plus narrower overrides; scripts/ is not in ignores) — the .mjs files are outside it and were passed explicitly, so this over-covers; ② the count comes from --format json: 0 errors, 0 warnings; ③ eslint.config.js has zero occurrences of projectService / parserOptions / project: (control: languageOptions matches once), so type-aware linting is off and no untouched file's verdict can depend on this diff.

check:eager-closure is unmoved, and this is structural rather than a byte reading. Locally the gate reports its own precondition — "No eager-closure report at apps/console/dist/eager-closure.json … a broken gauge, not a passing budget" — so it is NOT MEASURED here, not green. What is measured: 0 files in this diff are under apps/** or packages/*/src/**; the rest are scripts/ CI tooling and markdown. No source imports a README or a scripts/ file (the only ?raw imports in the tree are apps/console/index.html inside console tests; control: the same probe over d4c6a86a1 lists four real src/ files). Nothing here is a bundler input. CI confirms it: Bundle Analysis is green on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

…and stops teaching `stat-card`
The root `README.md` was outside the scan surface of EVERY doc gate.
`check-doc-component-types` walked `content/docs`; `check-doc-snippet-types`
walked `content/docs` plus the package READMEs; the most-read authored file in
the repository fell between the two. It taught `stat-card` four times, in the
flagship "dashboard in JSON" example, and `stat-card` is registered nowhere: a
reader who copied the headline snippet got four OBJUI-001 panels.
Maintainer ruling 2026-09-01 (verbatim 「同意」), in the ruled order:
1. The gate blind spot lands first — 「内容修了不加面就会再烂」.
2. The four widgets become `statistic`, which is registered
(`statistic.tsx:64`) and declares a `value` carriage row, so the example
keeps its expressions rather than degrading to literals.
3. The (A)/(B) fork is ruled B: non-`text` types get NO new carriage rows, and
the docs stop authoring `${…}` in keys that have none — 「文档教现实」.
Red-then-green, because a widened surface can be green about nothing: with
`ROOT_PAGES` added and the content untouched, `check-doc-component-types`
failed on `README.md:290`-`:293` naming `stat-card`; the content fix turned it
green. The same widening on `check-doc-snippet-types` red-flagged nine semantic
diagnostics, which are declared as measured debt in `UNGATED_DOCS` and filed as
objectui#7417 — the ledger names the file instead of the walk missing it.
Beyond the ruling's enumeration, and flagged rather than folded in silently:
`crud` (retired in objectui#5373) and `submit` were the other unregistered types
the widened gate found in the same file, and `card.value` carries no carriage
row. All four are the card's own defect classes in the card's own file; the PR
body names each with its evidence.
Fixes#7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation package: react plugin tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

…he third gate, found by its own coupling pin
`check-doc-fence-languages.test.ts` asserts `fenceDocuments(ROOT)` equals
`snippetDocuments(ROOT)` — the fence guard re-implements the snippet gate's walk
so it needs NO install, and that pin is what makes a deliberate copy safe. The
previous commit widened the snippet gate onto the root `README.md` and left the
copy behind, so the pin went red on 223 vs 224 documents.
That is the pin doing exactly its job, so it is not touched: the fence guard's
surface is widened instead, and its header now STATES the surface (objectui#5174's
own finding, applied here as it was to the other two).
`ROOT_PAGES` cannot be shared by import — anything imported from
`check-doc-snippet-types.mjs` pulls in its `import ts from 'typescript'` at load,
and this guard's whole value is running install-free in the unfiltered workflow a
docs-only PR starts. So it stays a copy, on the same bargain the document walk and
`TS_FENCE_LANGUAGES` already make: copy freely, compare always. All three gates now
EXPORT `ROOT_PAGES` and a new pin compares all three, so the shared half cannot
drift even where the full surfaces legitimately differ (component-types does not
walk the package READMEs).
⭐ The root README needed no fence fix: with it in the walk the guard reports 225
documents and zero findings for it, and the green is not vacuous — un-fencing one
of its `tsx` blocks in a census probe produces `README.md:206 language:'' mode:
synonym`. The objectui#5867 shrink-only baseline is untouched: no entry added.
This makes FOUR gates the root README's blind spot reached, now measured rather
than assumed — the card's thesis proving itself a second time.
Part of #7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 01:06
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 75e7436Sep 3, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7115-root-readme-doc-gate-surface branch September 3, 2026 01:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationpackage: reactplugintests

Projects

None yet

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card - #7419

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface
Sep 3, 2026
Merged

fix(docs,gates): the root README joins both doc gates' scan surface, and stops teaching stat-card#7419
os-project-manager merged 2 commits into
mainfrom
claude/issue-7115-root-readme-doc-gate-surface

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes#7115

The root README.md — the repository's GitHub landing page and the npm page for the workspace, the most-read authored file in it — was outside the scan surface of every doc gate. check-doc-component-types walked content/docs. check-doc-snippet-types walked content/docs plus the package READMEs. The root README fell between the two, and taught stat-card four times in its flagship "dashboard in JSON" example. Nothing registers stat-card, so a reader who copied the headline snippet got four OBJUI-001 "Unknown component type" panels.

The ruling this implements

Maintainer, 2026-09-01, verbatim 「同意」 (comment 5494799307), quoted in the ruled order:

  1. 门禁盲区:根 README.md 加进 check-doc-component-typescheck-doc-snippet-types 的扫描面(最高价值半边,先落——内容修了不加面就会再烂);
  2. 内容修:四个 stat-card widget 改 statistic(真实注册且有 value 运送行,"${…}" 照常求值,示例保持表达力);
  3. 叉裁 B:非 text 类型不加运送行 —— 停止在无运送行的键上编写 ${…}(文档教现实);真实应用要哪个键求值 ⇒ 带消费者立 carriage-row 卡,届时按条款②扩

All three parts, one PR (lane's choice, as the ruling permits).

Part 1 — the gate blind spot, proved red-then-green

Widening a scan surface is exactly the change that can be green about nothing, so the widening was landed red first and the evidence is here rather than asserted.

check-doc-component-types (ROOT_PAGES = ['README.md'], joined to scanDocs's file list):

statefiles scannedexitwhat it said
before, unwidened1850green — the README was not in the walk
widened, content untouched18617 findings, 4 of them the ruled ones
widened + content fixed1860green

The failure that proves the walk reaches the file, verbatim from the run:

 README.md:290 [unregistered-doc-type] type 'stat-card' (json)
{ "type": "stat-card", "title": "Revenue", "value": "${stats.revenue}", "trend": "+12%", "w": 3, "h": 1 },
README.md:291 [unregistered-doc-type] type 'stat-card' (json)
README.md:292 [unregistered-doc-type] type 'stat-card' (json)
README.md:293 [unregistered-doc-type] type 'stat-card' (json)

Ablation, to show the pins can fail. With the implementation committed, the files.push(...ROOT_PAGES…) line was deleted (anchored grep 1 to 0, blob 6688e098 to d0441c22 — confirmed on disk before the run, not asserted). The gate went back to 185 files and printed ✅ Every documented component type is registered — the exact "green about nothing" state — and both new pins went red with the intended message. Restored with git checkout HEAD -- ABSOLUTE_PATH under a trap … EXIT INT TERM, and the restore is verified by state: git diff HEAD empty and the blob equal to the HEAD blob, not by an exit code.

check-doc-snippet-types — the root README does carry snippets its rules cover (five tsx/typescript blocks), so the same proof applies and is not hand-waved. Widening it took the scan set from 224 to 225 documents, and the build closure the gate computes for itself grew by exactly @object-ui/providers — a second, independent signal that the walk now reaches the file. It went red with 9 semantic diagnostics.

⚠️Read this half honestly: those diagnostics are declared debt, not fixed.README.md is now named in UNGATED_DOCS with a measured reason, which is that gate's own designed mechanism and the objectui#5174 distinction its header states — a document outside the walk is "neither covered NOR declared ungated", invisible to the gate's own accounting, whereas a ledgered one is named, counted, re-derived every run and shrink-only. Three of the nine are real defects (ObjectRenderer, registerDefaultRenderers, createObjectStackAdapter attributed to the wrong package) and are filed as #7417, with the reason string in the ledger naming that card. Making those blocks compile means resolving two phantom APIs, which is authoring work with an API question inside it — not this card.

The third gate — check-doc-fence-languages, found by its own coupling pin

The ruling named two gates. There are four.check-doc-fence-languages re-implements the snippet gate's walk (deliberately, so it needs no install — the snippet gate imports typescript, and an install-gated docs check is one a docs-only PR skips), and check-doc-fence-languages.test.ts asserts fenceDocuments(ROOT)equalssnippetDocuments(ROOT). Widening one surface without the other breaks that coupling by construction, and it did: the pin went red at 223 vs 224 documents.

The pin was not touched — it is doing precisely the job it was written for, and it is the only reason the third gate was found at all. The fence guard's surface is widened instead, and its header now states the surface where a reader will find it, as objectui#5174's own finding requires.

ROOT_PAGEScannot be shared by import: anything imported from check-doc-snippet-types.mjs pulls in its import ts from 'typescript' at load, which would install-gate the one gate whose value is running install-free. So it stays a copy, on the bargain the document walk and TS_FENCE_LANGUAGES already make — copy freely, compare always. All three now exportROOT_PAGES and a new pin compares all three, so the shared half cannot drift even where the full surfaces legitimately differ (component-types deliberately does not walk the package READMEs, so it cannot join the list-equality comparison).

The root README needed no fence fix, and that green is not vacuous. With it in the walk the guard reports 225 documents and zero findings for it; the objectui#5867 shrink-only baseline is untouched, with no entry added. Control, through the shipped census(): un-fencing one of the README's tsx blocks produces { rel: 'README.md', line: 206, language: '', mode: 'synonym' }, so the file is genuinely parsed and genuinely judged.

Four gates, measured rather than assumed

The card's thesis was that the most-read authored file in the repository was checked by nothing. The true number turned out to be larger than the ruling enumerated, which is the thesis proving itself a second time:

gatestatus
check-doc-component-typeswidened here
check-doc-snippet-typeswidened here
check-readme-exportssame blind spot, surface is packages/NAME/README.md only — filed as #7417
check-doc-fence-languageswidened here, found by its own coupling pin rather than by anyone looking

Part 2 — the content fix

The four widgets become statistic, registered at packages/components/src/renderers/data-display/statistic.tsx:64. ⭐ A retarget, not a downgrade to literals: measured from the built artifact (expressionBindableTextKeysFor out of @objectstack/spec@17.2.0's dist/ui/index.mjs, the same lookup SchemaRenderer.tsx:1139 consumes), statistic carries [label, value, description], so "value": "${stats.revenue}" keeps evaluating.

Two keys moved with the type, because the renderer's real shape is not stat-card's: it reads schema.label (not title), and its trend is the enum up/down/neutral with the delta text in description. "trend": "+12%" would have rendered no arrow and no text — 文档教现实 applies to the retarget too.

Part 3 — fork ruled B, and no carriage row was added

Zero carriage rows added. The map lives in @objectstack/spec (a dependency), and this PR touches neither it nor SchemaRenderer.tsx.

Every ${…} rewritten, with the measured row set for its node type:

filenode typekeycarriage rows for that typenow
content/docs/guide/expressions.md (Percentage Bar)progressvaluenoneliteral 75, with visibleOn carrying the expression
content/docs/guide/expressions.md (Percentage Bar)progresslabelnoneliteral — same node, same defect, named on the card as :388
content/docs/guide/expressions.md (Computed Fields)inputvaluenoneshown by a text node's content, which is evaluated on every type
packages/plugin-dashboard/README.mdmetric-cardvaluenoneliteral, with a sentence pointing at the object-* widgets that do read live data
packages/plugin-dashboard/README.mdmetric-cardtrendnoneliteral 'up'
packages/react/README.mdinputvaluenonemoved to a sibling text node's content

On the card's own second-order note: it recorded, unmeasured, that metric-card and input "likely render the literal ${...}". That is now measured — both carry the empty set — but the rendering claim itself was never put in a browser and is not restated here as one.

⚠️ Deviations — flagged, not folded in silently

Widening a surface finds what the surface was hiding. Four things surfaced that the ruling's enumeration does not name, because the census behind it was taken before any gate had read this file. Each is stated so a reviewer can drop it on its own.

  1. crud × 2 — a RETIRED type. The widened gate found README.md:137 and :264 teaching crud. CRUDSchema and the crud node type were removed in objectui#5373 under ADR-0049; content/docs/api/schema-reference.md:541-:563 carries the retirement and an explicit "what to author instead" table, and validateSchema already returns RETIRED_TYPE for the name. The README was teaching a type the platform refuses by name. Retargeted per that table: the UserForm comparison onto object-form, the "Data Grid" example onto object-grid with that schema's real keys (field, not name, as its own doc row insists).
  2. submit — not a node type.README.md:256 authored "actions": [{ "type": "submit" }] under a form. FormSchema.actions is SchemaNode[], so this is a rendered node position, and ActionSchema.type is 'action' — the authentication.mdx exemption for submit does not reach here. The declared key for the built-in submit button is submitLabel, which is what it now says.
  3. card.value × 3 — ruling B, in a site the ruling's census never read. The "Basic Usage" snippet bound ${stats.*} in card.value; card carries [title, description], so value there is a literal. Retargeted onto statistic — the same move part 2 ordered, for the same reason.
  4. Findings left alone and filed rather than fixed: finding(docs): the root README's ts/tsx snippets do not compile — 3 names on no built dist/index.d.ts, incl. createObjectStackAdapter attributed to the wrong package #7417 (the root README's snippet debt, plus check-readme-exports' identical blind spot) and finding(docs): guide/expressions.md still authors ${…} in 6 more keys with no carriage row — badge.text / badge.variant / card.className / card.body, beyond the two the 2026-09-01 ruling named #7418 (six more non-carriage ${…} in guide/expressions.md beyond the two the ruling enumerated).

Items 1-3 are the card's own defect classes in the card's own file, and 1-2 are structurally forced: part 1 cannot land green while the file it now reads is red. They are bounded, and each correct form is pinned by evidence already in the tree — nothing here is a judgement call about an API.

Verification

All runs at 02efff4ba, the final commit, with a clean tree.

checkresult
check:doc-types✅ 186 files, 922 type literals, 787 registered
check:doc-snippets✅ 225 documents, 193 covered, 32 ungated, 417/417 blocks judged
check:doc-fences225 documents, root README contributing 0 findings (control fires — see above)
docs:check-links✅ 17 scan roots
check:readme-exports✅ 406 self-imports judged, 0 fabricated
check:control-bytes✅ 6109 files
check:pre-install-import-graph✅ 22 modules walked, every non-relative leaf a node builtin — the fence guard stays install-free
changeset:check + check-changeset-presence✅ — "no changeset is owed"; an empty-frontmatter declaration is here anyway, per the objectui#7114 precedent
the three gate suites + 7 derived tests✅ 10 files, 226 tests (the three gate suites carry +10 new pins)
eslint --no-inline-config over the changed lintable files✅ 0 errors, 0 warnings

On the eslint narrowing, stated so it reads as a measurement rather than a skip: ① the receiving population is read from eslint.config.js itself (files: ['**/*.{ts,tsx}'] plus narrower overrides; scripts/ is not in ignores) — the .mjs files are outside it and were passed explicitly, so this over-covers; ② the count comes from --format json: 0 errors, 0 warnings; ③ eslint.config.js has zero occurrences of projectService / parserOptions / project: (control: languageOptions matches once), so type-aware linting is off and no untouched file's verdict can depend on this diff.

check:eager-closure is unmoved, and this is structural rather than a byte reading. Locally the gate reports its own precondition — "No eager-closure report at apps/console/dist/eager-closure.json … a broken gauge, not a passing budget" — so it is NOT MEASURED here, not green. What is measured: 0 files in this diff are under apps/** or packages/*/src/**; the rest are scripts/ CI tooling and markdown. No source imports a README or a scripts/ file (the only ?raw imports in the tree are apps/console/index.html inside console tests; control: the same probe over d4c6a86a1 lists four real src/ files). Nothing here is a bundler input. CI confirms it: Bundle Analysis is green on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC

…and stops teaching `stat-card`
The root `README.md` was outside the scan surface of EVERY doc gate.
`check-doc-component-types` walked `content/docs`; `check-doc-snippet-types`
walked `content/docs` plus the package READMEs; the most-read authored file in
the repository fell between the two. It taught `stat-card` four times, in the
flagship "dashboard in JSON" example, and `stat-card` is registered nowhere: a
reader who copied the headline snippet got four OBJUI-001 panels.
Maintainer ruling 2026-09-01 (verbatim 「同意」), in the ruled order:
1. The gate blind spot lands first — 「内容修了不加面就会再烂」.
2. The four widgets become `statistic`, which is registered
(`statistic.tsx:64`) and declares a `value` carriage row, so the example
keeps its expressions rather than degrading to literals.
3. The (A)/(B) fork is ruled B: non-`text` types get NO new carriage rows, and
the docs stop authoring `${…}` in keys that have none — 「文档教现实」.
Red-then-green, because a widened surface can be green about nothing: with
`ROOT_PAGES` added and the content untouched, `check-doc-component-types`
failed on `README.md:290`-`:293` naming `stat-card`; the content fix turned it
green. The same widening on `check-doc-snippet-types` red-flagged nine semantic
diagnostics, which are declared as measured debt in `UNGATED_DOCS` and filed as
objectui#7417 — the ledger names the file instead of the walk missing it.
Beyond the ruling's enumeration, and flagged rather than folded in silently:
`crud` (retired in objectui#5373) and `submit` were the other unregistered types
the widened gate found in the same file, and `card.value` carries no carriage
row. All four are the card's own defect classes in the card's own file; the PR
body names each with its evidence.
Fixes#7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation package: react plugin tests labels Sep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

…he third gate, found by its own coupling pin
`check-doc-fence-languages.test.ts` asserts `fenceDocuments(ROOT)` equals
`snippetDocuments(ROOT)` — the fence guard re-implements the snippet gate's walk
so it needs NO install, and that pin is what makes a deliberate copy safe. The
previous commit widened the snippet gate onto the root `README.md` and left the
copy behind, so the pin went red on 223 vs 224 documents.
That is the pin doing exactly its job, so it is not touched: the fence guard's
surface is widened instead, and its header now STATES the surface (objectui#5174's
own finding, applied here as it was to the other two).
`ROOT_PAGES` cannot be shared by import — anything imported from
`check-doc-snippet-types.mjs` pulls in its `import ts from 'typescript'` at load,
and this guard's whole value is running install-free in the unfiltered workflow a
docs-only PR starts. So it stays a copy, on the same bargain the document walk and
`TS_FENCE_LANGUAGES` already make: copy freely, compare always. All three gates now
EXPORT `ROOT_PAGES` and a new pin compares all three, so the shared half cannot
drift even where the full surfaces legitimately differ (component-types does not
walk the package READMEs).
⭐ The root README needed no fence fix: with it in the walk the guard reports 225
documents and zero findings for it, and the green is not vacuous — un-fencing one
of its `tsx` blocks in a census probe produces `README.md:206 language:'' mode:
synonym`. The objectui#5867 shrink-only baseline is untouched: no entry added.
This makes FOUR gates the root README's blind spot reached, now measured rather
than assumed — the card's thesis proving itself a second time.
Part of #7115
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3178.3 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-XRCMG0Ou.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.67KB5.75KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)514.87KB117.50KB
core (index.js)5.80KB2.32KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)178.20KB49.60KB
fields (index.js)244.08KB61.71KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)47.00KB12.97KB
plugin-charts (index.js)70.31KB19.55KB
plugin-chatbot (index.js)196.19KB46.43KB
plugin-dashboard (index.js)132.63KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)251.20KB64.18KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)132.87KB32.66KB
plugin-gantt (index.js)166.93KB40.82KB
plugin-grid (index.js)209.10KB56.65KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.51KB27.67KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)30.21KB8.66KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.74KB2.25KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)8.11KB3.32KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 01:06
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 75e7436Sep 3, 2026
32 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7115-root-readme-doc-gate-surface branch September 3, 2026 01:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationpackage: reactplugintests

Projects

None yet

2 participants

@os-project-manager@claude