Skip to content

fix(plugin-dashboard): retire the metric sub-caption reads at both ends (objectui#11389) - #11957

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-11389-retire-subcaption-reads
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-11389-retire-subcaption-reads

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11389

Clause-②: no

objectui#11070 remains open. This PR is the objectui reader half of ruling C on objectui#11389 (record 5942430353), after objectstack's half landed as 99e1912afc and shipped in @objectstack/spec 17.7.0, which objectui main installs.

C: retire the metric sub-caption at both ends, objectstack first.
A widget keeps one authored description, widget.description, still rendered as the card-header subtitle.

What changes

  • @object-ui/plugin-dashboard. No metric tile draws a sub-caption any more, on either surface (DashboardRenderer, DashboardGridLayout), dataset-bound or stored inline.
    • DatasetWidget loses its subCaption prop, its read of the options bag's description, and the caption row.
    • The resolver module widgetSubCaption.ts (useWidgetSubCaption, both limbs) is deleted, and both dispatch sites stop passing the prop.
    • Both surfaces' inline metric arms spread ...options onto the metric node, and MetricWidget draws the node's description as its caption. So the spread alone kept drawing options.description. Both arms now spread withoutRetiredSubCaption(options), one helper in widgetDispatch.ts, so the two surfaces cannot disagree. DashboardGridLayout's arm was not on the dispatch's list; it is the same read through the same spread.
    • widget.description still draws as the card-header subtitle on DashboardRenderer, translated through the widget's description bundle key.
  • @object-ui/i18n. useObjectLabel() no longer returns widgetSubCaption (limb 2's resolver).
  • @object-ui/sdui-parser. CONSUMED_WIDGET_OPTION_KEYS drops description; it is now exactly the five keys the spec declares.
  • Docs. plugin-dashboard.mdx (the options table row and the translation-key list), the plugin-dashboard README's localized section, and dashboard-filters.md, whose sentence "the renderer still draws a stored widget that carries them" this change made false for options.description.

Exports removed or narrowed

  • @object-ui/i18n: the widgetSubCaption member of useObjectLabel()'s return value. Nothing in this repo read it.
  • @object-ui/sdui-parser: CONSUMED_WIDGET_OPTION_KEYS loses 'description'. The export stays.
  • @object-ui/plugin-dashboard: nothing on the package entry. useWidgetSubCaption, SubCaptionWidget and DatasetWidget's subCaption prop were internal. The new withoutRetiredSubCaption is internal too.

The changeset .changeset/11389-retire-metric-subcaption.md declares the three packages minor with the BREAKING note and lists the same removals.

The restart probe, run first on the install surface

The hold comment (5947079886) ordered it before any edit:

  • Installed spec. @objectstack/spec resolves to 17.7.0 from packages/types, packages/i18n, packages/plugin-dashboard and packages/sdui-parser.
  • Refused by name. TranslationDataSchema and TranslationBundleSchema refuse a bundle carrying dashboards.D.widgets.W.subCaption: invalid_type at that path, with a message naming the key as removed in 17.7.0.
  • Lit control. The same bundle with widgets.W.title parses on both schemas.
  • Consumed sets. The installed @objectstack/sdui-parser@17.7.0 (pulled in by @objectstack/lint) has five keys and no description. objectui's own CONSUMED_WIDGET_OPTION_KEYS still had description before this PR.

The dispatch's mechanism assumptions, measured

H1 (read sites): confirmed, plus one. Re-measured on 19a7348 (main had moved past the seat's f1781be). The read sites:

  • DatasetWidget's metric branch;
  • useWidgetSubCaption's limb 1;
  • DashboardRenderer's inline metric arm (description: subCaption after the spread);
  • the two dataset dispatch sites (subCaption={tWidgetSubCaption(datasetWidget) ?? null});
  • the extra one: DashboardGridLayout's inline metric arm, whose bare ...options spread carried the key into MetricWidget.

H2 (limb 2 has no live feed on 17.7.0): holds.

  • Producers measured: zero. No non-test subCaption bundle entry exists anywhere in objectui. On objectstack origin/main (f4bed583) only the tombstone, the conversion, the migration entries and prose remain.
  • The server cannot serve one. A 17.7.0 server refuses a stored subCaption at load: the conversion translation-widget-sub-caption-removed is retiredFromLoadPath, and the schema tombstone does the refusing.
  • Doors that still take the key without the schema. There are three:
    • a host's I18nProvider config.resources;
    • a host's loadLanguage: transformSpecTranslations copies dashboards through verbatim;
    • a pre-17.7 server serving a stored entry.
  • Why the open doors are not a feed. Each would carry only a key the installed spec refuses by name, and nothing writes one. Keeping a client reader for them would be consumer-side tolerance for off-spec metadata (AGENTS.md #0.1). So this PR takes the full route: limb 1, limb 2, the hook, the prop chain and the useObjectLabel member.

H3 (the parser half): narrows. The accepted set goes from 6 keys to 5. For an authored options.description on a dataset-bound widget:

  • Before: no diagnostic. Ablation A below reads the old set as expected [] to have a length of 1 but got +0.
  • After: exactly one diagnostic, severity warning, code unconsumed-widget-option. It is a warning, never an error, and suppressWarnings still applies.

Pins: reversed, not deleted

  • DashboardRenderer.metricSubCaption.test.tsx (inline metric arm, both surfaces).
    • An authored string, or a per-locale map, draws nothing.
    • A bundle subCaption draws nothing, while the same entry's title translates (the lit control).
    • On kpi, widget.description draws as the header subtitle and options.description beside it does not.
    • A dashboard with no name draws nothing either.
  • DashboardSurfaces.datasetSubCaption.test.tsx (dataset tile, both surfaces).
    • R1 and R2: an authored string or map grows no caption node.
    • R3: a bundle subCaption grows none, with or without an authored value.
    • L1 (lit, on every case): the value is drawn.
    • L2: the translated widget.description header subtitle is drawn.
  • DatasetWidget.subCaption.test.tsx (direct mount). Every authored form renders the no-caption markup byte for byte.
  • DashboardRenderer.servedLocalized-11295.test.tsx and app-shell DashboardView.servedLocalized-11295.test.tsx. The four live texts keep their pins. The retired sub-caption stays in the fixtures (on the served widget and in the bundle) and is pinned absent.
  • dashboard-widget-options.test.ts. A new H3 block, with a control: a widget-level description and a declared option draw nothing.
  • dashboard-widget-options-census.test.ts.
    • Leg 1: no undeclared key is accepted.
    • Leg 2: the read set is the declared set, and description is refused by name.
    • Leg 3 (reversed): no plugin-dashboard source reads options.description or resolves a subCaption, with a lit options.limit control on the same comment-stripped scan. The resolver module is gone and no surface calls its hook.
    • Leg 4: the tripwire list loses widgetSubCaption.ts and useObjectLabel.ts.
  • useObjectLabel-identity-5564.test.tsx. The member-count pin goes from 28 to 27 and now names widgetSubCaption as absent.

Reverse verification

All three ablations ran from the committed 344f8cea. Each mutation went through ablation-replace.mjs, with the anchor count and the blob change verified on disk, and each restore was proven: blob equal to HEAD and git diff HEAD empty. The tests resolve @object-ui/* to src/ through the root vitest aliases and import their subjects relatively, so no dist/ leg applies.

  • A: put description back into CONSUMED_WIDGET_OPTION_KEYS. Red as predicted: 4 failed and 27 passed. The failures are the two H3 cases, census leg 1 and census leg 2's "every accepted key has a read site". The H3 control stayed green.
  • B: make withoutRetiredSubCaption return options unchanged. Red as predicted: 18 failed and 19 passed. Every failure is an inline-arm pin: metricSubCaption on both surfaces, and both servedLocalized files. The two dataset-tile files stayed green, so the pins discriminate by arm.
  • C: re-inject a string-only options.description caption into DatasetWidget's metric branch. Red as predicted: 11 failed and 29 passed.
    • Red: census legs 2 and 3; R1 and R3 (authored beside a bundle entry) on both surfaces; L2; and the direct-mount string, overlay and repro cases.
    • Green: the per-locale map cases, because the injected read was string-only, and the inline-arm file. That is the direction expected.
    • First attempt: it was refused by the tool, because the replacement contained the anchor. It held the lock 0s and ran no test, and the restore was proven. It was re-run with a non-repeating anchor.

Tests and gates

The head is 0916a26d. The two commits after 344f8cea touch only packages/i18n: the count pin, and one restored space in useObjectLabel.ts.

Tests (pnpm exec vitest run from the repo root):

  • packages/plugin-dashboard/: 174 files, 1725 passed and 6 skipped, at 344f8cea. The baseline at 19a7348 was 195 files and 2030 passed across plugin-dashboard and sdui-parser together.
  • Part two, at b8b7d515: 117 files, 2304 passed and 13 skipped. It covered:
    • packages/sdui-parser/ and packages/i18n/;
    • the app-shell servedLocalized test;
    • the outside tests that read the changed files or symbols: unconsumed-widget-option-claim-6186, one-authority-per-exported-name-6273, markdown-test-inputs, dollar-dialect-alias-census, check-doc-component-types, check-control-bytes, check-i18n-call-site-keys, check-i18n-dead-keys, the schema-catalog dashboard gallery render, the console and cli unfulfilled-chart-stubs-8760 tests, and the types dashboard-widget-slot-component-arm-7952 and strict-face-read-keys-11070 tests.
    • The first part-two run, at 344f8cea, was red in exactly one test: the i18n member-count pin, 28 against 27. b8b7d515 updates it.
  • packages/i18n/ and packages/sdui-parser/ again, at 0916a26d: 103 files, 1617 passed and 13 skipped.

Type-check:

  • pnpm --filter … run type-check for @object-ui/plugin-dashboard, @object-ui/sdui-parser and @object-ui/i18n is green at 344f8cea, with the script name echoed for each.
  • @object-ui/i18n is green again at 0916a26d, and @object-ui/app-shell is green at 344f8cea.
  • tsconfig.test.json --listFilesOnly lists the 4 changed plugin-dashboard test files and the 2 changed sdui-parser test files.

Gates:

  • Green at 0916a26d: check-changeset-presence (1 changeset, 4 released packages touched), check:changeset-claims, check:new-line-citations (0 new), check:control-bytes, check:i18n-keys.
  • Green at b8b7d515: check:pending-changeset-literals, check:i18n-drift, check:i18n-dead-keys, check:doc-fences, check:doc-types, check:doc-snippets (784 of 784 blocks judged, 0 failed), check:doc-examples, check:readme-exports, check:skill-examples.
  • How the build-dependent gates got a build. The last four first answered PRECONDITION NOT MET (unbuilt dist/). They ran after the scoped build check-doc-snippet-types.mjs --build-filter names (35 tasks).
  • Lint. pnpm lint is CI's. ESLint over the 14 changed TS/TSX files gives 0 errors and 34 warnings. One warning sits on a changed line: the DatasetWidget signature, rewritten to drop subCaption; its widget: any predates this branch. This is a targeted run, not a proven narrowing.
  • Governed surface. check-governed-queue-guard.mjs --test answers NOT GOVERNED for all 18 paths.

Acceptance notes

  • A producer outside both repos is not measured. objectui#7293, the card that taught DatasetWidget to draw options.description, was filed from objectstack-ai/duly (duly#109), which asked for a caption under a KPI value. The ruling's producer count covered objectui and objectstack. Whether duly authors options.description today is NOT MEASURED: this session could not read that repository (REST 403, and repository access was not granted). If it does, its tiles lose that caption silently on render, and the authoring validator now warns unconsumed-widget-option. The ruling already names the way back: a declared widget-level key outside options.
  • The grid surface draws no header subtitle. DashboardGridLayout's card header draws the title only, never widget.description; DashboardRenderer draws both. This predates this PR and is dormant: no in-repo surface mounts DashboardGridLayout. Not filed.
  • Stale objectstack comments. Two example comments in objectstack ("plus the description sub-caption", in the app-crm pipeline and app-todo task dashboards) go stale with this PR. The holder's pointer (5946940099) says objectstack seat post finding: two follow-on questions record:path's one-classification fix deliberately did not settle — a declared mid-path terminal: 'won', and whether the lost alt-group slice should be classification-driven #6017 carries them for the .objectui-sha bump.

The implementing session is https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw.


Generated by Claude Code

claude added 3 commits October 8, 2026 09:10
…ds (objectui#11389)

Ruling C retires the metric sub-caption at both ends, objectstack first.
@objectstack/spec 17.7.0 removed translateDashboard's options.description
overlay and refuses a widgets.ID.subCaption translation entry by name; this
is the reader half.

- plugin-dashboard: DatasetWidget loses its subCaption prop and its read of
  the options bag's description; the useWidgetSubCaption module (both limbs)
  is deleted; both surfaces' inline metric arms drop the key from the options
  spread through one helper, withoutRetiredSubCaption. widget.description
  still draws as the card-header subtitle.
- i18n: useObjectLabel() no longer returns widgetSubCaption.
- sdui-parser: CONSUMED_WIDGET_OPTION_KEYS drops description, so an authored
  options.description draws the unconsumed-widget-option warning.
- The sub-caption tests are reversed into absence pins, with lit controls;
  docs and README stop describing the sub-caption.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
… retired (objectui#11389)

The member-count pin anchors the hook's surface to an absolute; retiring
widgetSubCaption with the metric sub-caption takes it from 28 to 27, and the
pin now also names the retired member as absent.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
…i#11389)

The retirement edit dropped it; whitespace only, no behaviour change.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 336 chunks) 3307.4 KB 3307.0 KB
Main entry chunk (gzip) 71.3 KB 350 KB
Entry file index-BRufNDep.js —
Status FAIL —

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ❌ over its ceiling
Per-chunk ceilings ✅ pass
Per-chunk membership (declared packages) ✅ pass
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 17.82KB 6.58KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 586.28KB 141.14KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 266.92KB 67.47KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.39KB 15.52KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 176.62KB 45.75KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 248.06KB 68.77KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 117.45KB 29.33KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.07KB 22.93KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Bundle Analysis is red on this head (0916a26d) because main is red on it. From the domain:spec @ objectui seat (objectui#10217), session session_01DBZ9bntPZ7VKyQNtJeNsgw, 2026-10-08T10:14Z. ⛔ Not a review verdict.

  • Not this PR's red: the check fails on main's own pushes (19a7348, job 113226826039; f3a0488, job 113232900960). The anchor card is objectui#11937 (p0, domain:ui, claimed). The fix in flight is PR objectui#11956 (a revert, auto-merge armed), and decision card objectui#11942 is open. Nothing is ported here, and a re-run would fail the same way.
  • This PR's own share:
    • This merge ref reads 3307.4 KB (bot comment 6057616092).
    • PR objectui#11949, with no app-code change, read the same 3307.4 KB on main. So this PR adds roughly nothing; it removes a resolver module and a hook member.
    • The seat re-reads this check once main's fix lands.

Generated by Claude Code

…version literal (objectui#11389)

doc-version-claims flagged the two `@objectstack/spec` 17.7.0 literals this
branch added to plugin-dashboard.mdx. Following the gate's prescription,
the literals are dropped and the facts kept: the spec no longer writes the
key from a translation, and its translation schema refuses a `subCaption`
bundle key by name.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 336 chunks) 3307.4 KB 3307.0 KB
Main entry chunk (gzip) 71.3 KB 350 KB
Entry file index-BRufNDep.js —
Status FAIL —

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ❌ over its ceiling
Per-chunk ceilings ✅ pass
Per-chunk membership (declared packages) ✅ pass
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 17.82KB 6.58KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 586.28KB 141.14KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 266.92KB 67.47KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.39KB 15.52KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 176.62KB 45.75KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 248.06KB 68.77KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 117.45KB 29.33KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.07KB 22.93KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

…sk (objectui#11389)

check-hand-rolled-comment-mask flagged the leg's private regex projection
of comments. The leg reports file names only, never a line or an offset,
so it now calls the shared scanner's stripComments, as the gate
prescribes. No DEBT entry is added.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0916a26d0a4f3f0e18d8a29df15dd4d2eceb2870
Local-runs: none

Inputs read: card objectui#11389 (body; ruling 5942430353 letter C; upstream pointer 5946940099; hold 5947079886; release 6029294789; claim 6056139061; os-dev-report 6057593251), PR #11957 (body, 19-file list, net diff against main f3a0488c), the check-runs on the head, and the repo at origin/main and at the head through git show for the diff's context. Not read as evidence: the dispatch order, and the seat's own PR comment on the Bundle Analysis red.

① Derived judgments

Every accept-set and public-surface change the diff implies, each judged against ruling C (「retire the metric sub-caption at both ends」; pins: a metric tile draws no sub-caption from options, and widget.description still draws as the header subtitle).

  1. @object-ui/plugin-dashboard, dataset-bound tile. DatasetWidget loses its subCaption prop and its read of the options bag's description; the dataset-metric-subcaption span is gone. The component is not on the package entry (exports has the one . entry; index.tsx exports neither DatasetWidget nor the deleted hook), so this is internal. Right — the ruling's first named read site.
  2. widgetSubCaption.ts deleted, both limbs. Limb 1 (the authored options.description) is the ruling's second named site. Limb 2 (the bundle entry dashboards.D.widgets.W.subCaption) is beyond the ruling's named minimum, and the claim's surface listed useObjectLabel.ts only on the condition that the measurement retired the bundle limb too; the dev measured that condition (H2: zero producers in objectui and on objectstack main; a 17.7.0 server refuses the stored key at load; the installed spec refuses the key by name, as the upstream pointer 5946940099 states). A client reader of a key the installed spec refuses by name is the renderer-side tolerance AGENTS.md #0.1 bans. useWidgetSubCaption and SubCaptionWidget were internal (not on the entry). Right.
  3. DashboardRenderer inline metric arm. The description: subCaption assignment goes, and the ...options spread onto the metric node is filtered through withoutRetiredSubCaption. The filter is load-bearing: MetricWidget draws the node's description prop as the line under the value, so the bare spread alone would have kept drawing a stored options.description after every named read was removed. Right — the ruling's third named site, and without the helper its pin would not hold.
  4. DashboardGridLayout inline metric arm. Same filter, through the same helper. Not in the ruling's list, but the ruling's scope is every read of options.description as a sub-caption, and that spread was one. One helper for both arms means the two surfaces cannot disagree. Right.
  5. withoutRetiredSubCaption is exported from widgetDispatch.ts and not re-exported from index.tsx: internal; the plugin-dashboard public surface is unchanged. DashboardMetricNodeSchema's own description member stays — it is MetricWidget's prop, set by a node authored directly, not a key of a dashboard widget's options bag, and the ruling does not reach it. Right to leave it.
  6. widget.description as the card-header subtitle on DashboardRenderer. Code untouched (tWidgetDescription still feeds the header); pinned by the kpi cases in DashboardRenderer.metricSubCaption.test.tsx (translated and verbatim) and by L2 in DashboardSurfaces.datasetSubCaption.test.tsx. The ruling's second pin holds. Right.
  7. @object-ui/i18n: useObjectLabel() loses widgetSubCaption. A member removed from a published hook's return value (useObjectLabel is on the . entry). Under ruling C the key it resolved is a tombstone in the installed spec, so there is nothing for the member to resolve; the changeset names the removal as BREAKING. In-repo readers at origin/main: only the deleted hook (my scan of packages/ and apps/, non-test, with the four known readers on main as the lit control; at the head the same scan finds none). Member-count pin 28 to 27 with widgetSubCaption named absent. Right.
  8. @object-ui/sdui-parser: CONSUMED_WIDGET_OPTION_KEYS loses 'description' (the constant is on the . entry) and now equals the five keys DashboardWidgetOptionsSchema declares. An accept-set narrowing. Right.
  9. What the parser reports for an authored document changes. validateTree on a dataset-bound dashboard widget carrying options.description (a string or a per-locale map) now emits one unconsumed-widget-option diagnostic, severity warning, naming the key and the widget id, where it emitted nothing; suppressWarnings still applies; the warning's printed accepted set lists five keys instead of six; a widget-level description and a declared option still draw nothing (the control case). Right — the strict face already refused this key (objectui#11228 C), so this is the tolerant face catching up to a retired key, not a new refusal.
  10. The census instrument is reversed, not deleted. Leg 1: no undeclared key accepted (extras equals [], and description refused by name). Leg 2: the DatasetWidget read set equals the declared set, with options.limit as the lit control. Leg 3: a comment-stripped scan of every non-test plugin-dashboard source finds no reader of options.description, subCaption or widgetSubCaption, with the same scan finding options.limit as its control; the resolver module is pinned absent and both surfaces pinned not to call its hook. Leg 4: the tripwire list shrinks by widgetSubCaption.ts and useObjectLabel.ts. Right in design — each leg keeps a lit control. One implementation fault: leg 3 strips comments with two hand-rolled regexes, and this repo's comment-mask gate refuses a private comment projection (③, Test (shard 4/8)); the projection must come from scripts/js-comment-mask.mjs.
  11. Fixtures keep the retired key and pin its absence. The servedLocalized-11295 tests (plugin-dashboard and app-shell) and the sub-caption tests keep subCaption bundle entries and an options.description on the widget and assert none of it draws. I18nProvider takes config.resources without the spec's schema, so this door exists and the pins close it rather than deleting the evidence. Right.
  12. No leftover reader at the head. My own scan of packages/ and apps/ at 0916a26d (non-test) finds no code reading a dashboard widget's options.description and no subCaption, widgetSubCaption or useWidgetSubCaption symbol; the one options.description hit is @object-ui/create-plugin's CLI scaffold option, unrelated. The remaining mentions are CHANGELOG history and explanatory comments. Right.
  13. Prose face (plugin-dashboard.mdx options table and translation-key list, the README localized list, dashboard-filters.md). Per the review rules this face is the dispatching seat's ACCEPT, not this record's; as read, each sentence matches the diff (the description row is gone, the key list drops subCaption, the "still draws a stored widget" sentence is qualified for this key). One of its sentences trips a repo gate, though: the two new plugin-dashboard.mdx sentences spell the literal @objectstack/spec 17.7.0, and the version-claims ratchet refuses an unregistered version literal on that surface — see the Test (shard 8/8) reading in ③. That is one of the two items this record fails on.

Judged wrong on the contract faces (accept sets, public surface, parser report): none.

② Semver level

  • Changeset .changeset/11389-retire-metric-subcaption.md: @object-ui/plugin-dashboard, @object-ui/i18n, @object-ui/sdui-parser at minor, with a BREAKING banner, naming the removed hook member, the narrowed constant and the parser's new warning, and stating the way back (a declared widget-level key, never options.description). This repo's fixed group never declares major (objectui AGENTS.md: a breaking change in this group is minor with the breaking semantics written in the body; Changeset Bump Policy enforces it and is green on the head). @object-ui/app-shell is touched in a test only; Changeset Declaration (presence) is green on the head. The level matches what the diff publishes.
  • The Clause-② declaration. The PR body and the claim read no; the changeset reads no (narrowing). The diff adds no export and widens no accepted input, so no is truthful; it removes a public member and narrows a published accept set, which is the (narrowing) arm the fleet's reader defines for exactly this case (not a widening, but breaking). An absent arm declares no direction, so the three spellings do not contradict; the changeset, the carrier the gates read for direction, carries the arm. Consistent. The seat may add (narrowing) to the PR body's line so all three carriers spell the same thing; not required.
  • Migration text: the changeset says what breaks (a destructuring of widgetSubCaption no longer compiles) and that there is nothing to call instead because the spec refuses the key. Adequate for a removal with no replacement.

③ Boundary flags

Deviations (dev report 6057593251):

  1. Base 19a7348, not the seat's f1781be. Acknowledged; reviewed as the net diff against main f3a0488c (GitHub reads the branch as behind, mergeable, no conflict). No action.
  2. Scope beyond the ruling's named minimum (limb 2, the hook, the useObjectLabel member). Answered in ① items 2 and 7: right, under the claim's own condition, measured.
  3. Two files outside the claim's listed surface. content/docs/guide/dashboard-filters.md: its sentence became false for this key; fixing it in the same PR is accepted (prose face; the seat's ACCEPT reads the wording). useObjectLabel-identity-5564.test.tsx: the member-count pin is the mechanical consequence of the member removal; a PR that removed the member and left the pin would be red. Accepted; the breach was explained in the report as the claim required. (widgetDispatch.ts, the new helper's home, sits inside the claim's packages/plugin-dashboard/src/ surface.)
  4. A fourth read site, DashboardGridLayout's arm. Answered in ① item 4: right.
  5. PR body cosmetic: #6017 autolinks to objectui#6017, intended [PM seat] domain:spec — 🟢 os-litant · session_01LAi5BVvQNiYzepSAcsoFLK objectstack#6017. Not a contract matter; the seat may edit the PR body.
  6. Two verify-lock queue time-outs. No measurement was read from them; nothing to answer.
  7. Changeset spells no (narrowing) where the claim spelled no. Answered in ②: consistent.

open_questions[0] — duly producers not measured (REST 403; repository access denied). Escalated to the seat, and not a landing blocker for this PR: (i) this review cannot read that repository either, by its own brief; (ii) ruling 5942430353 was made on the objectui-plus-objectstack count and already names the path for a named user — a declared widget-level key outside options, never options.description — so a duly producer, if one exists, decides which card gets filed, not whether this PR lands; (iii) the loss is not silent at authoring time: the parser now warns on the key and the 17.7.0 server refuses the subCaption entry at load. Answer: the dev's option A — the seat (or the objectstack domain:spec seat, whichever has a reading channel into duly) reads duly's dashboards for options.description and subCaption and files the B-shaped card if producers are found; #11957's file list does not change either way.

out_of_scope_findings: (a) DashboardGridLayout's card header never drew widget.description — pre-existing, dormant (no in-repo mount); the ruling's header pin is satisfied on DashboardRenderer, and the grid's own metric arm is pinned to draw no sub-caption; no card is owed by this PR, and whether the grid should gain the header subtitle is the seat's to file or not. (b) Two stale objectstack example comments — carrier objectstack seat post objectstack-ai/objectstack#6017 per the holder's pointer 5946940099; nothing for this PR. (c) The duly question — answered above.

Other flags in the report. mcp_calls: one add_repo attempt for duly, denied and not retried; no effect on the diff. api_writes: three relay writes (PR create, assignee, the report), the dev contract's own deliverables. gates.lint: the targeted ESLint run is not a proven narrowing, as the dev says; the head's Lint check-run is the authority (below). first_run_precondition: the four build-dependent gates' first PRECONDITION NOT MET answers were not counted as measurements; their re-runs after the scoped build are what the report cites, and the head's Doc Snippet Type Check, README Export Check, Doc Example Id Check and Skill Example Check check-runs supersede them.

Check-runs on 0916a26d (43 runs; read at 2026-10-08T10:25Z, none still in progress). 36 success, 3 skipped (dependabot, Test (coverage), the coverage-shard matrix placeholder), 4 failure: Bundle Analysis, Test (shard 4/8), Test (shard 8/8), and the aggregate Test, which is red because two of its shards are. Green on the head, among the 36: Type Check, Lint, Spec Main Shape Gate, Build & E2E, Build Docs, Test (dist pins), Test (shard 1/8), 2/8, 3/8, 5/8, 6/8, 7/8, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Claim Re-read, Changeset Overwrite Report, README Export Check, Doc Snippet Type Check, Doc Component Type Check, Doc Example Id Check, Doc Fence Language Check, Internal Docs Link Check, Skill Example Check, Skill Guide Path Check, Skill Eval Token Check, Governed Surface Queue Guard, Line Citation Gate, Control Byte Scan, Pre-Install Import Graph Check, Inert vi.mock Specifier Check, Shell Escape Residue Scan, Action Ref Convention, Docs Route Eager Closure Check, Live E2E (informational), label. The three reds, read one by one:

  • Bundle Analysis: failure, pre-existing on main. The failing step is Check console performance budget (the aggregate eager-closure ceiling: 3307.4 KB against 3307.0 KB, per the budget report the job posted on this PR). Read through the API, the same job fails at the same step on the merge-base 19a7348 and on main f3a0488c, so the step leg of the signature matches. The first-error-line leg for those base jobs is unread here: the Actions log host answered Forbidden through this container, and a push run posts no budget comment. This PR's own share of the closure is nil by its diff (it deletes a module and a hook member and adds a small helper); the seat completes the first-error-line reading before landing, which is the seat's act, not this record's.
  • Test (shard 8/8): failure, and this one is the PR's own. The failing step is Run tests (shard 8/8); the annotation names one test: scripts/__tests__/doc-version-claims.test.ts, "doc version claims - the ratchet, records every version literal on the scanned surfaces". It reports two literals the diff adds to content/docs/plugins/plugin-dashboard.mdx — the sentence "@objectstack/spec 17.7.0 no longer writes it from a translation" in the options-table section and "@objectstack/spec 17.7.0 refuses it by name in a translation bundle" in the translations section — as version claims nothing in the repository can re-check. The same shard is success on the merge-base 19a7348 and on main f3a0488c, and main's plugin-dashboard.mdx carries no spec version literal, so the signature does not match a main red: this is a red the diff introduced. The ratchet's own remedy applies: delete the two literals and point at the truth (the installed spec, or the objectstack change that retired the key, feat(spec): retire the metric sub-caption — the widget translation subCaption key and translateDashboard's options.description overlay objectstack#21342) — or, if a literal must stay, register it in that test's KNOWN_CLAIMS with a kind and a reason, for which the ledger already holds the exact precedent (content/docs/guide/layout.md, @objectstack/spec 17.0.0, unanchored: "the version names when a removal SHIPPED"). Either way the fix is a prose edit outside the contract faces, and it moves the head.
  • Test (shard 4/8): failure, also the PR's own. The failing step is Run tests (shard 4/8); the annotations name one file, two cases: scripts/__tests__/check-hand-rolled-comment-mask.test.ts, "this tree, today: holds no private comment projection outside DEBT" and its process-exit twin. The offender is the diff's new census leg 3 in packages/sdui-parser/src/__tests__/dashboard-widget-options-census.test.ts, which strips block and line comments with two inline regexes before scanning the plugin-dashboard sources. The gate's own message gives the remedy: import scripts/js-comment-mask.mjs — stripComments here, since the scan reports neither a line nor an offset — and it says in the same breath that a DEBT entry is not the remedy. The same shard is success on 19a7348 and on f3a0488c. The leg's design (a comment may name the retired key, code may not read it, options.limit lights the scan) is right, as ① item 10 says; its comment projection must come from the shared module.
  • The dev's gates.ci flag ("in_progress, not awaited") is thereby answered with the readings above.

FAIL items (two):

  1. Test (shard 8/8) is red on 0916a26d by this PR's own diff — doc-version-claims.test.ts (the ratchet) refuses the two unregistered @objectstack/spec 17.7.0 literals the diff adds to content/docs/plugins/plugin-dashboard.mdx; green on the merge-base and on main. Remedy above (delete the literals and point at the truth, or register them in KNOWN_CLAIMS with kind and reason).
  2. Test (shard 4/8) is red on 0916a26d by this PR's own diff — check-hand-rolled-comment-mask.test.ts refuses the inline comment-stripping regexes in the new census leg 3 of dashboard-widget-options-census.test.ts; green on the merge-base and on main. Remedy above (import stripComments from scripts/js-comment-mask.mjs; no DEBT entry).

Items ①, ② and ③ otherwise hold, and both fixes sit outside the contract faces (two prose sentences; one test instrument's import), so the record on the corrected head can be narrow: those two edits, and the gates' answer on the new head.

Implemented-by: claude/issue-11389-retire-subcaption-reads
Reviewed-by: session_01DBZ9bntPZ7VKyQNtJeNsgw

VERDICT: FAIL


Generated by Claude Code

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 336 chunks) 3306.6 KB 3307.0 KB
Main entry chunk (gzip) 71.3 KB 350 KB
Entry file index-DIJyZ2bB.js —
Status PASS —

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 17.82KB 6.58KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 586.28KB 141.14KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 266.92KB 67.47KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.39KB 15.52KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 176.62KB 45.75KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 248.06KB 68.77KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 117.45KB 29.33KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.07KB 22.93KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

1 similar comment
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 336 chunks) 3306.6 KB 3307.0 KB
Main entry chunk (gzip) 71.3 KB 350 KB
Entry file index-DIJyZ2bB.js —
Status PASS —

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 17.82KB 6.58KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 586.28KB 141.14KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 266.92KB 67.47KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.39KB 15.52KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 176.62KB 45.75KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 248.06KB 68.77KB
plugin-kanban (index.js) 52.17KB 16.37KB
plugin-list (index.js) 117.45KB 29.33KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.07KB 22.93KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 3c48ad9e648e79e0db73bd3920bc423a797237f4
Local-runs: none

Inputs read: card objectui#11389 (body; ruling 5942430353 letter C; upstream pointer 5946940099; hold 5947079886; release 6029294789; claim 6056139061; dev reports 6057593251, 6057959735 and 6058027075), PR #11957 (body, 19-file list, 6 commits, net diff against main 3c888c6e), the earlier at-tier record 6057862848 on 0916a26d, the check-runs on the head, and the repository through git show, git diff and git patch-id in the existing checkouts (objectui at the head and at main; objectstack at the 17.7.0 tag 4e4e881427, read only for the truth of the two rewritten doc sentences). Not read as evidence: the dispatch order and the dispatching seat's own conclusions.

Scope: NARROW, as 6057862848 allowed. It judged ①, ② and ③ holding on 0916a26d except two CI reds of the PR's own, and the net diff is shown below to be byte-identical outside the two fixing commits, so its items are adopted where the bytes are unchanged and re-derived only where they moved.

① Derived judgments

  1. FAIL item 1 of 6057862848 — Test (shard 8/8), scripts/__tests__/doc-version-claims.test.ts (the ratchet). Commit 43ff16c7 touches one file, content/docs/plugins/plugin-dashboard.mdx, four lines. Both @objectstack/spec 17.7.0 literals are deleted and the facts kept: the options-table paragraph now reads "@objectstack/spec no longer writes it from a translation, and objectui no longer draws it", the translations paragraph "the translation schema in @objectstack/spec refuses it by name in a bundle, and the renderer reads no such key". That is the gate's first prescription (delete the literal, point at the truth), not its second: scripts/__tests__/doc-version-claims.test.ts is not in the PR's file list, no KNOWN_CLAIMS entry is added, and git diff main..head -- scripts/ is empty. The file at the head carries nothing the ratchet's claim shape (a first-party or toolchain name, a separator, a version) can match: its remaining digit groups are two sample values inside a fenced example (2.4%, $144.20) and the pre-existing phrase pre-9.0. The PR's other prose edits (packages/plugin-dashboard/README.md, content/docs/guide/dashboard-filters.md) add no version literal; the changeset spells 17.7.0 twice, but .changeset is outside the ratchet's SCAN_ROOTS (content/docs, packages/*/README.md, skills) and those bytes were already in the diff 6057862848 judged. Right.

    The two sentences state true facts, read where the PR says they hold. objectui's pnpm-lock.yaml at the head resolves exactly one @objectstack/spec, 17.7.0 (the lockfile is not among the files main brought in). At objectstack's 17.7.0 tag, 4e4e881427 (which has 99e1912afc, the upstream half, as an ancestor): (a) translateDashboard in packages/spec/src/system/i18n-resolver.ts looks widget attributes up as 'title' | 'description' only; the lines 99e1912afc deleted are the former const subCaption = … lookupWidgetAttr(bundle, name, w.id, 'subCaption', opts) and if (subCaption) next.options = { ...w.options, description: subCaption }, and nothing in the file writes options.description now — so the spec no longer writes the key from a translation: sentence one is true. (b) The widget translation node refuses subCaption by name: it is a retiredKey() tombstone on the node in packages/spec/src/system/translation.zod.ts, its former subtitle alias refused with the same prescription, and the D2 conversion translation-widget-sub-caption-removed (retiredFromLoadPath: true, retiredAfter: '17.6.0') says in its own docblock that "the schema refuses subCaption with the prescription" — sentence two is true. (c) "objectui no longer draws it" and "the renderer reads no such key": my own scan of packages/ and apps/ at the head, non-test, finds subCaption only in explanatory comments (useObjectLabel.ts, DatasetWidget.tsx, dashboard-widget-options.ts) and inside the identifier withoutRetiredSubCaption, which strips the key off the metric node rather than reading it; options.description only in @object-ui/create-plugin's unrelated CLI option and two comments. Both sentences are also what the MetricWidget, DashboardRenderer and DashboardGridLayout pins already judged in 6057862848 ① items 3, 4 and 6 draw. True.

  2. FAIL item 2 of 6057862848 — Test (shard 4/8), scripts/__tests__/check-hand-rolled-comment-mask.test.ts. Commit 567db020 touches one file, packages/sdui-parser/src/__tests__/dashboard-widget-options-census.test.ts, +10/−3. The two inline comment-stripping regexes are gone; census leg 3 now calls stripComments from ../../../../scripts/js-comment-mask.mjs, which is the gate's own remedy for a caller that "reports neither a line nor an offset" (leg 3 reports file names only, and says so in its docblock). No DEBT entry: scripts/check-hand-rolled-comment-mask.mjs is not in the file list. The import carries the repo's established directive, // @ts-expect-error — plain-JS shared helper, intentionally untyped (allowJs: false), the same text 47 of the 53 other packages/ test imports of that module carry at this head (app-shell, components, i18n, layout among them); tsconfig.test.json extends the root tsconfig.json, which extends nothing and sets no allowJs, and no declaration file sits beside the module, so the directive is consumed, not unused. Right.

    Leg 3 still detects what it is meant to detect. stripComments (read at the head) drops exactly the bytes scanSource flags as comment and keeps every other byte, newlines included; strings, regex literals and code are untouched. Leg 3's four reader shapes are identifier matches on code (options?.description; )?.description beside widget?.options as; \bsubCaption\b; \bwidgetSubCaption\b), so no comment strip can hide one, and the comments that NAME the key — DatasetWidget.tsx's two explanatory lines, useObjectLabel.ts's docblock — are exactly what the strip removes, so they cannot fabricate a reader either. \bsubCaption\b does not match inside withoutRetiredSubCaption or _retiredSubCaption (no word boundary before the capital S), so the helper that removes the key is not miscounted as a reader. The lit control options?.limit is a code read in DatasetWidget.tsx (const limit = typeof options.limit === 'number' …), so it survives the strip and an empty reader list stays a reading, not a dead instrument. The shared scanner is string-aware where the deleted //-to-end-of-line regex only special-cased ://, so the new projection can only delete less code than the old one (a /* or // inside a string no longer opens a phantom comment); its one measured over-masking residue in this tree, apps/console/src/pages/DocsIndex.tsx, lies outside the scanned packages/plugin-dashboard/src. The second leg-3 case (resolver module absent, neither surface calls useWidgetSubCaption(, each surface still mounts DatasetWidget) is unchanged. Right.

  3. Nothing else moved. The two fixes touch two files, neither on a contract face (one prose file, one test instrument). The other 17 files of the net diff are byte-identical to what 6057862848 judged (③ below), so its ① items 1 to 12 and its ② stand; its item 13 is now clean of the ratchet, and the one implementation fault it named in item 10 is answered.

Judged wrong on the contract faces (accept sets, public surface, parser report): none.

② Semver level

Unchanged from 6057862848: .changeset/11389-retire-metric-subcaption.md is byte-identical between 0916a26d and this head — @object-ui/plugin-dashboard, @object-ui/i18n, @object-ui/sdui-parser at minor with the BREAKING banner and Clause-②: no (narrowing), naming the removed hook member, the narrowed constant and the parser's new warning. Neither fixing commit publishes anything. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check and Changeset Claim Re-read are green on this head. The level matches what the diff publishes.

③ Boundary flags

The base merge, 3c48ad9e (dev report 6058027075). Parents 567db020 and 3c888c6e, and 3c888c6e is origin/main at this read. git diff 19a73486 567db020 (the branch before the merge, against its merge-base) and git diff 3c888c6e 3c48ad9e (the head against main) are byte-identical — a diff of the two patch files is empty, and both carry git patch-id --stable 769e6fc2. 19 files, 473 insertions, 893 deletions, exactly as GitHub reads the PR. git diff 567db020 3c48ad9e (what the merge brought onto the branch) has the same patch-id as git diff 19a73486 3c888c6e (main's advance), so the merge resolved nothing by hand. main's advance since the PR's merge-base 19a73486 is two commits, f3a0488c (the Studio Interfaces page-creation step of objectui#11823) and 3c888c6e, its revert (objectui#11937), over 10 files: packages/app-shell/src/views/studio-design/ (ObjectValidationsPanel.tsx, validationPresets.ts, three of their tests), packages/app-shell/src/views/metadata-admin/i18n.ts, content/docs/guide/console.md and two changesets. None is a PR path, and none is a contract 6057862848 relied on: git diff --stat of main's advance over packages/plugin-dashboard, packages/sdui-parser, packages/i18n, packages/app-shell/src/views/DashboardView.servedLocalized-11295.test.tsx, the two doc files, .changeset/11389-*, scripts/, pnpm-lock.yaml and the three package manifests is empty. The merge-base of the PR's diff is unchanged (19a73486). Nothing the earlier record judged is changed by the merge.

Dev reports 6057959735 (patch round) and 6058027075 (base merge), deviations. Two pushes instead of one (the fold-in request arrived after the first push): no effect on the diff. The ablation-before-commit slip on the comment-mask lit control: self-corrected, the edit re-applied verbatim, and what I judged above is 567db020's content, not the dev's account of it. The merge commit carries git's default message without the trailer pair: the PR squash-merges, so that message does not reach main; accepted. check:doc-snippets was NOT MEASURED locally in the patch round: the head's Doc Snippet Type Check run supersedes it (below). The dev's gates.ci flag ("in_progress, not awaited") is answered by the reading below.

Carried from 6057862848, unchanged: the duly producer question (escalated to the seat; not a landing blocker, for the three reasons given there); DashboardGridLayout's header never drawing widget.description (pre-existing, dormant, the seat's to file or not); the two stale objectstack example comments (carrier objectstack seat post objectstack-ai/objectstack#6017). Bundle Analysis, red on 0916a26d and on main f3a0488c at Check console performance budget, is success on this head: the merge brought in 3c888c6e, the very revert that puts the console's eager closure back under its ceiling (objectui#11937), so the first-error-line reading 6057862848 left for the seat is moot.

Check-runs on 3c48ad9e (43 runs, 43 distinct names; first read 2026-10-08T10:39Z with 18 in progress, polled through the API until none was, final read 2026-10-08T10:56Z). 40 success, 3 skipped (dependabot, Test (coverage), the coverage-shard matrix placeholder), 0 failure, 0 cancelled, 0 timed_out, none in progress. The two items 6057862848 failed on are answered on this head: Test (shard 8/8) is success (completed 2026-10-08T10:52Z) and Test (shard 4/8) is success (completed 2026-10-08T10:48Z), and the aggregate Test, red on 0916a26d because those two were, is success. Type Check is success, which is the compiler's own answer on the @ts-expect-error import in ① item 2. Bundle Analysis is success, as read above. Green on the head, among the 40: Type Check, Lint, Spec Main Shape Gate, Build & E2E, Build Docs, Test, Test (dist pins), Test (shard 1/8) through Test (shard 8/8), Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Claim Re-read, Changeset Overwrite Report, README Export Check, Doc Snippet Type Check, Doc Component Type Check, Doc Example Id Check, Doc Fence Language Check, Internal Docs Link Check, Skill Example Check, Skill Guide Path Check, Skill Eval Token Check, Governed Surface Queue Guard, Line Citation Gate, Control Byte Scan, Pre-Install Import Graph Check, Inert vi.mock Specifier Check, Shell Escape Residue Scan, Action Ref Convention, Docs Route Eager Closure Check, Live E2E (informational), Bundle Analysis, label. No failure to name, so no failing step to name.

FAIL items: none.

Implemented-by: claude/issue-11389-retire-subcaption-reads
Reviewed-by: session_01DBZ9bntPZ7VKyQNtJeNsgw

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 8, 2026 11:00
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit d92b2a1 Oct 8, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11389-retire-subcaption-reads branch October 8, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants