Skip to content

test(components): ratchet the container-declaration census to zero - #6808

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6779-container-declaration-ratchet
Aug 30, 2026
Merged

test(components): ratchet the container-declaration census to zero#6808
os-sam merged 1 commit into
mainfrom
claude/issue-6779-container-declaration-ratchet

Conversation

@claude

@claudeclaudeBot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Fixes#6779

Implements the 2026-08-29 maintainer ruling (option B): the runtime census pin
made universal, plus an explicit ratchet-to-zero list of the remaining
stock. No registration metadata changes in this PR, so nothing PR #6774
declared is reverted and no new tag is declared.

What lands

  1. packages/components/src/renderers/__tests__/container-declaration-ratchet.test.tsx
    — objectui#6740's mechanism (render the tag through the real SchemaRenderer
    with one authored child, then run validateTree over the manifest the app
    builds) with its coverage set changed from a literal 4-element array to
    the whole registry.
  2. scripts/container-declaration-baseline.json — the 44 remaining violations,
    shaped after i18n-call-site-key-baseline.json / skills-path-baseline.json:
    a note block that states the shrink-only contract, a dated since column on
    every entry, and the button exclusion recorded with the ruling that made it.
  3. An empty-frontmatter changeset: test-only, publishes nothing.

RECOUNTED on my own base (main@d06059f24), not inherited

cardmeasured here
registry keys292293
bare authoring tags131131
render schema.children5858
declare isContainer513
do NOT declare5345
public among the undeclared1 (button)1 (button)
render failures / unresolved tags00

The control holds: the five the card named — flex / grid / card /
container / stack — all still declare, and the other 8 declarers are exactly
PR #6774's set, which is why 13 here and 5 there describe the same tree at two
different refs. 45 undeclared minus button = 44 listed, matching the
ruling's predicted remainder exactly.

The exception shapes need no skip-list — reporting rather than narrowing

The ruling requires that tabs, the void tags (img / hr / br) and the
body-only family are not swept in. Measured: every one of them already scores
as a non-container
, because none puts an authored child on the page. The
runtime predicate excludes them by construction; nothing is skipped by name.
They are pinned anyway — as pins on the predicate, so that rewriting it into
the source-side "renders children OR body" spelling goes red instead of silently
turning 13-plus tags into containers.

One recount delta worth recording: the body-only family is 13 bare keys, not
the 10 the card estimated — the sidebar- family is 11 keys, not 8. It
changes nothing here (none of them violates), but it changes objectui#6771's
denominator.

button is excluded by name and pinned as a live description — still renders
children, still undeclared, still the only public one. When the separate card
lands, that pin goes red and the exclusion has to be resolved rather than left
standing, which is the tail risk the triage seat recorded against option B.

A new violation turns it red — verified in both directions

Both legs mutated a committed tree, proved the mutation on disk by anchored
grep -c (never an editor exit code), ran under a trap restore with absolute
paths, and were restored back to the HEAD blob hash. No rebuild was needed: the
mutated files are same-package sources reached through the test's relative
import '../index', not through any dist.

A — a new undeclared container. Deleted the shared isContainer: true from
renderers/layout/semantic.tsx (blob c4eccd6a to 7b5e59ad, anchor count 1 to 0):

AssertionError: new undeclared container(s) - declare `isContainer` on the
registration, do not list them: expected [ 'article', 'aside', 'footer', ...(4) ]
to deeply equal []
+ [ "article", "aside", "footer", "header", "main", "nav", "section" ]
Tests 1 failed | 11 passed (12)

B — a paid-off entry left on the list. Added isContainer: true to
scroll-area without deleting its baseline line (blob 94ed7678 to c30ff42c,
injected-text count 0 to 1):

AssertionError: these no longer violate - delete their lines from the baseline
(the list may only shrink): expected [ 'scroll-area' ] to deeply equal []
Tests 1 failed | 11 passed (12)

Exactly one assertion failed in each leg and the other 11 stayed green, so each
red is attributable. After restore, git diff HEAD is empty and both blob hashes
match HEAD.

Verification, on the final commit bf8c6b116

One &&-joined chain, verdict command-exit 0:

  • tsc --noEmit and tsc -p tsconfig.test.json --noEmit in packages/components
  • eslint on the new test file
  • check-control-bytes (OK, 5647 tracked text files)
  • check-changeset-presence (empty frontmatter ... a complete answer to this gate)
  • check-changeset-no-major
  • vitest over the new pin and both existing pins it sits beside:
    Test Files 3 passed (3) / Tests 45 passed (45)

Also green separately: check:self-import, check:phantom-deps,
check:esm-specifiers, check:vi-mock-specifiers, check:side-effects-array,
check:shell-escape-residue.

Declared narrowing. eslint was run on the changed files rather than
repo-wide. Read from eslint's own config resolution (--format json, 3 paths
presented, 1 linted): the JSON and the changeset are reported as
File ignored because no matching configuration was supplied, i.e. outside
eslint's population by its own config, not by my guess. eslint.config.js
declares no projectService and no parserOptions.project, and no package
carries its own eslint config, so type-aware linting is off and this diff cannot
move the verdict of any untouched file. CI runs the full farm regardless.

NOT MEASURED:check:sdui-registration-pins. It exits 2 with
PREREQUISITE NOT MET until the console bundle is built, and that build needs
the full package graph (33 of 40 packages have no dist in this worktree); it
failed in packages/app-shell, which this diff does not reach — the whole diff
is 3 added files. Recording it as not measured rather than as a red. CI builds
the graph and runs it.

Scope

Per the ruling, out of scope and deliberately untouched: button (excluded, the
PM seat files its card), and the body-only family (badge, alert, the
sidebar- keys), which belong to objectui#6771's separate ruling.


Generated by Claude Code

The same defect has been found three times independently, one registration at
a time (objectui#3900, objectui#6740, objectui#6764), because both existing
pins are ENUMERATIVE -- a literal 4-element array and the 8 that PR #6774
declared -- so the class regenerates past them.
Adds the universal form of objectui#6740's runtime mechanism: every bare
authoring tag in the registry is rendered through the real SchemaRenderer with
one authored child and put through validateTree, and a registration that
renders that child while omitting `isContainer` fails the run.
Measured on main@d06059f24: 293 registry keys, 131 bare tags, 58 render
children, 13 declare the flag (objectui#6764's control set plus PR #6774's 8),
45 do not. `button` is excluded by the 2026-08-29 ruling and pinned separately;
the other 44 are scripts/container-declaration-baseline.json, a ratchet to zero
that is red in both directions -- an unlisted violator fails, and so does a
listed tag that no longer violates -- so the list can only shrink.
No registration metadata changes, so nothing is declared or reverted here.
Refs objectui#6779
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 45 chunks)3174.0 KB3222.7 KB
Main entry chunk (gzip)148.1 KB350 KB
Entry fileindex-BFe99tIZ.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)11.89KB4.50KB
app-shell (runtime-config.js)20.61KB7.35KB
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)511.50KB116.32KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)240.93KB60.76KB
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.95KB10.97KB
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)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
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)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)64.68KB18.35KB
plugin-chatbot (index.js)190.33KB45.10KB
plugin-dashboard (index.js)133.48KB34.51KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)245.46KB62.46KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.03KB32.64KB
plugin-gantt (index.js)165.23KB40.37KB
plugin-grid (index.js)201.57KB54.55KB
plugin-kanban (index.js)53.14KB14.64KB
plugin-list (index.js)113.15KB27.59KB
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)28.95KB8.33KB
plugin-tree (index.js)9.00KB3.08KB
plugin-view (index.js)85.87KB21.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)76.75KB25.49KB
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.72KB2.24KB
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)3.40KB1.71KB
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

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Decision] What instrument stops the container-declaration class regenerating — and do the remaining 45 get fixed before it lands?

2 participants

@os-sam@claude