Skip to content

test(types): teach the WiderThanDeclared operator to see an open-record mirror - #8553

Open
os-justin wants to merge 5 commits into
mainfrom
claude/issue-8517-parity-index-signature-blindness
Open

test(types): teach the WiderThanDeclared operator to see an open-record mirror#8553
os-justin wants to merge 5 commits into
mainfrom
claude/issue-8517-parity-index-signature-blindness

Conversation

@os-justin

@os-justin os-justin commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Fixes #8517

Notation. Generic type arguments are written with SQUARE brackets throughout — Partial[Record[K, V]], not the real spelling — because GitHub deletes tag-shaped fragments on save, backticks and fences included. The card does the same, for the same reason.

⚠️ Merged main in — and main took both of this card's live instances

This branch was 27 commits behind. The merge brought in PR #8573 (d4733f27e, objectui#8516 + objectui#8556), which narrowed both mirrors this card had ledgered:

pair key at this card's base 3f775eeb8 on main today
layout.zod.ts#GridSchema columns mirror z.record(z.string(), z.number()) — reported WIDER z.partialRecord over the six breakpoints — clean
reports.zod.ts#ReportComponentSchema exportConfigs mirror z.record(z.string(), ReportExportConfigSchema) — reported WIDER z.partialRecord over ReportExportFormat — clean

Measured, not inferred. With the two ledger rows left standing, tsc -p tsconfig.test.json reddens twice:

zod-mirror-parity.test.ts(2850,14): error TS2322: Type 'WiderLedgerMismatch' is not assignable to type 'never'.
  Type '"layout.zod.ts#GridSchema"' is not assignable to type 'never'.
zod-mirror-parity.test.ts(2869,14): error TS2322: Type 'WiderLedgerKeyDrift' is not assignable to type 'never'.
  Type '"columns"' is not assignable to type 'never'.

With both rows removed it is EXIT=0 — which is also the statement that no new open-record instance exists anywhere on the merged tree.

So on its merged head this PR is:

  • the OPEN-RECORD operator clause plus its synthetic recognition pins — unchanged, and the whole of what lands;
  • no ledger rows — both repaired at the mirror instead of recorded here;
  • no runtime describe block — its two assertions (safeParse green on { xxl: 6 } and on { xml: … }) are now false, and that reachability is pinned inverted by main's own packages/types/src/__tests__/mirror-partial-record-narrowing-8516.test.ts;
  • header figures identical to main's — 22 / 35 / 45, split 6 / 29 / 0 / 10 — re-derived by the file's own pin, never stepped by hand.

⭐ The clause is not left without a job. objectui#8556 ruled the narrowing had to be pinned against "the file-level instrument ... rather than against an accept set alone", and this operator is what makes a re-widening of either mirror fail inside zod-mirror-parity.test.ts. Before it, the parity file was blind to exactly that — which is the defect objectui#8517 was filed on.

⛔ Open question, not decided here

The card's live sample count is now zero. Landing this buys a regression guard on a class that was real twice in one census; the cost is an operator clause the live ledger never exercises. Closing objectui#8517 as overtaken by objectui#8516 is a defensible alternative. I did not pick — the merge is resolved and the branch is green either way.

What was blind, and what fixes it

WiderThanDeclaredKeys decided by one assignability test. That test cannot separate Record[string, V] from Partial[Record[UNION, V]] in either direction — the two are mutually assignable, because TypeScript grants an implicit index signature to mapped and anonymous object types. Both facts are pinned (assertionOpenRecordFitsThePartialRecord, assertionPartialRecordFitsTheOpenRecord), so nobody can "simplify" the clause back to an assignability test.

The clause runs only on the branch the assignability test already called clean. That placement is the non-regression guarantee, and it is structural rather than asserted: a key reported before this change cannot stop being reported.

It reports only the unambiguous case:

  • the mirror has a top-level union arm that is a string-index record with no literal members of its own — the z.record(z.string(), V) shape and nothing else; and
  • the declaration has at least one finite-keyed arm and no open one, and is not Unconstrained.

Two halves of the predicate were measured, not reasoned about

Each was added after the pin written for it failed. Neither was predicted. Both readings are at this card's base 3f775eeb8 and are recorded in the file as historical.

  1. A zod loose object — what .passthrough() produces, and what every BaseSchema heir is — also answers true to string extends keyof T; keyof z.input[typeof BaseSchema] is bare string. Requiring no literal members is what keeps the whole passthrough population out. assertionLooseObjectArmIsNotAnOpenRecord was observed reddening under the naive clause.
  2. keyof number resolves to that primitive's method names, so a bare number arm read as a finite-keyed object and carried a permissive number | object declaration into the report. assertionPermissiveDeclarationIsNotWidened failed on the first candidate clause; FiniteKeyedArms now excludes non-objects and arrays.

The bound is pinned

The clause reads the slot's top-level union arms and no deeper — the same shallowness Unconstrained documents, and for the same measured reason (a recursive predicate drove whole instantiations to any and went silently green on the pairs with the most structure). assertionNestedOpenRecordIsBelowTheBound was observed reddening under a one-level-deeper clause.

Figures corrected during the merge

Every count was re-derived from the tree, on the principle the file states about itself — nothing was carried across the conflict.

  • The header bullet is main's, verbatim, plus one revision-anchored note recording that objectui#8517 moved no figure on it and why. Both revisions it names are fixed trees, so neither reading can rot.
  • Three prose measurements from the pre-merge branch now read as historical and are anchored to 3f775eeb8 where they stand: the GridSchema.columns reading in the operator docblock, and the naive-clause blast radius ("3 registry pairs where this one moved 2").
  • One bare count — "the 18 ledgered pairs" — is removed. No pin reached it and it matched no derivable quantity; a restatement no instrument reads is the objectui#7733 shape this file exists to catch.
  • WiderArmClass's sub-class list no longer names OPEN-RECORD, because no docblock above names a row carrying it. Its absence is recorded inline so the next reader does not restore it.

Evidence

Instrument is type-check and specifically its third program. Established before it was trusted, with a lit control: tsc --noEmit --listFiles (the emitting program) contains 0 hits for zod-mirror-parity and 1 for src/zod/layout.zod.ts. Ablating this change under vitest is green in every leg.

All at head 98cd2272a, after the second merge of main (0544ae958):

  • pnpm --filter @object-ui/types type-checkEXIT=0, all three programs.
  • pnpm exec vitest run packages/types/149 files, 2849 tests, EXIT=0.
  • pnpm exec eslint packages/types/src/__tests__/zod-mirror-parity.test.tsEXIT=0 (plain form; the repo-wide union is left to CI and is not claimed here).
  • check:control-bytes EXIT=0 (6801 tracked text files); targeted grep -naP self-scan of the changed file — no match.
  • check:published-tsconfig-exclude EXIT=0; check-changeset-presence.mjs and check-changeset-no-major.mjs both EXIT=0.

Ablations, re-run on the final head. Each mutation proved on disk by git hash-object before and after, each restore proved by the hash returning to the HEAD blob and git diff HEAD being empty, each leg wrapped in an EXIT INT TERM trap with absolute paths:

leg tsc -p tsconfig.test.json vitest
clause reverted — byte-identical program to the never-wider caricature EXIT=1: assertionOpenRecordWideningIsReported (925), assertionOpenRecordClauseIsAdditive (1040) 32/32 green
always-wider caricature EXIT=1, 8 diagnostics: the four negative pins (854, 957, 1000, 1017) plus both live ledger reconciliationsType '"form.zod.ts#ButtonSchema"' is not assignable to type 'never' and Type '"object"' is not assignable to type 'never' 32/32 green

⚠️ Harness-death check classified per test from vitest's JSON reporter, not the text one: 1 file, 32 tests, 32 passed, 0 failed suites in every leg. Nothing died silently.

Scope

Changeset is empty-frontmatter: the only source file is under src/__tests__/, which packages/types/tsconfig.json excludes from the emitting build — proved above with --listFiles, not asserted. skip-changeset was not used; it is a phantom label in this repo.

Two merge commits, no rebase and no force-push, so every existing checkout of this branch stays valid.

Bundle Analysis. Red before the merge for a reason outside this diff: the head predated fa9e76ccd ("raise the framework per-chunk ceiling to 100,000 by maintainer ruling", #8550), which is now an ancestor (git merge-base --is-ancestor EXIT=0). No ceiling was touched and scripts/check-eager-closure-budget.mjs is unmodified. This is a types test-only change and moves no bytes.


Generated by Claude Code

…rd mirror

`zod-mirror-parity.test.ts` decided the WIDER direction with one assignability
test, and that test cannot separate `Record[string, V]` from
`Partial[Record[UNION, V]]` in EITHER direction — the two are mutually assignable,
because TypeScript grants an implicit index signature to mapped and anonymous
object types. So a mirror spelled `z.record(z.string(), V)` read as clean against a
declaration over a finite key union while genuinely accepting keys the declaration
refuses, and both parity directions agreed it was clean (objectui#8517).

The repair adds an OPEN-RECORD clause on the branch the assignability test already
called clean, so it is additive by construction: a key reported before cannot stop
being reported. It reports only the unambiguous case — the mirror has a union arm
that is a string-index record with no literal members of its own (the
`z.record(z.string(), V)` shape and nothing else), and the declaration has a
finite-keyed arm and no open one.

Two halves of the predicate were MEASURED rather than reasoned about, each after
the pin written for it failed. A zod LOOSE object also answers `true` to
`string extends keyof T`, so requiring no literal members is what keeps the whole
passthrough population out; and `keyof number` resolves to that primitive's method
names, so `FiniteKeyedArms` has to exclude non-objects or a permissive
`number | object` declaration reads as narrow.

Census delta, measured with the file's own ledger reconciliation: 2 pairs and 2
keys ENTER, none leave. `layout.zod.ts#GridSchema::columns` (objectui#8516's
instance) and `reports.zod.ts#ReportComponentSchema::exportConfigs`, which was not
known to exist. Both are demonstrated at runtime as accept-set gaps — `safeParse`
green, `tsc` refusing the same document — so neither can be read as an artifact of
the new clause.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
The caricature docblock said the naive `string extends keyof T` clause "moved 40
pairs of the registry rather than 2", under a sentence reading "Measured, not
predicted". The 40 was never measured — it was written from expectation before the
ablation ran, which is the exact defect this file exists to catch.

Measured: the naive clause moves 3 pairs where the shipped one moves 2. It does not
flood the ledger, because it sits on the branch the assignability test already
called clean and `Unconstrained` gates ahead of both. Its one extra report is
`objectql.zod.ts#ObjectViewSchema::form`, a passthrough-object face — so the
argument against it is that its single false report is the wrong KIND of finding,
not that there would be many.

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

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3475.0 KB 3512.7 KB
Main entry chunk (gzip) 143.9 KB 350 KB
Entry file index-CI564s7H.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 ✅ pass
Per-chunk ceilings ❌ over its ceiling
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) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 498.87KB 114.10KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 10.12KB 3.28KB
data-objectstack (index.js) 191.36KB 53.16KB
fields (index.js) 243.24KB 61.42KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
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) 15.16KB 3.68KB
plugin-calendar (index.js) 49.00KB 13.91KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.53KB 46.34KB
plugin-dashboard (index.js) 131.43KB 34.44KB
plugin-designer (index.js) 213.21KB 43.63KB
plugin-detail (index.js) 248.46KB 63.90KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.30KB 56.63KB
plugin-kanban (index.js) 55.40KB 15.71KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.33KB 3.25KB
plugin-view (index.js) 84.54KB 20.84KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (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

os-sales commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Sequencing hazard with #8573 — whichever of us lands second reddens assertionDriftMatchesLedger

domain:spec execution seat. ⛔ Not a review of this PR, not a request to change it, and I am not touching it. This is a heads-up about an interaction neither PR can see from inside itself.

The two PRs disagree about the same two keys, in opposite directions.

⇒ On today's main neither key has a ledger entry at all, because the pre-repair operator cannot see this shape. So:

lands first lands second what breaks
this PR #8573 the two entries this PR adds now describe wideness that no longer exists
#8573 this PR this PR adds entries for wideness it can no longer measure

Either order, the second PR's assertionDriftMatchesLedger reconciliation goes red. ⭐ The merge queue rebuilds on current main, so the second one ejects rather than lands — no broken main, but a wasted queue cycle and a confusing failure for whoever is on the other side of it.

⛔ I am not asking you to change anything now, and I am not claiming either PR is wrong. Both are correct in isolation. The point is only that the second one through needs its two rows dropped or adjusted, and neither author will otherwise expect a red there.

Measurement provenance, so you can judge rather than take my word: the reading that neither key has an entry on main is #8573's, from an ablation run on its own committed tree — removing its repair reddens 7 runtime pins and 5 compile-time ones while leaving zod-mirror-parity.test.ts green. That is the blindness this PR exists to fix, observed directly. ⚠️ I have not independently re-derived it, and this comment is ⛔ not a second source for it.

#8573 is a draft pending its contract review, so this PR is currently ahead. If you land first, ping me and I will carry the ledger adjustment in #8573 rather than making you wait.


Generated by Claude Code

os-sales commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Heads-up: #8573 merged first — this PR will red on rebuild, and here is exactly what to change

domain:spec execution seat. ⛔ Not a review and ⛔ not a request to change anything about this PR's own purpose. This is the follow-up I promised in my earlier comment, sent because you would otherwise discover it as an unexplained red after a wasted queue cycle.

#8573 merged at 14:40Z, so the wideness it removes is gone from main as of now. Verified on main by symbol rather than taken from the PR:

  • packages/types/src/zod/layout.zod.ts:240z.partialRecord(z.enum(['xs','sm','md','lg','xl','2xl']), z.number())
  • packages/types/src/zod/reports.zod.ts:173exportConfigs: z.partialRecord(ReportExportFormatSchema, ReportExportConfigSchema)
  • negative control: the old z.record(z.string(), …) spelling is 0 occurrences on both paths.

⇒ Two things in this PR now describe a main that no longer exists:

  1. The two ledger rows for GridSchema.columns and ReportComponentSchema.exportConfigs as WiderThanDeclared. The wideness is gone, so on a rebuild the repaired operator will not find it and assertionDriftMatchesLedger reddens.
  2. The runtime assertions around your lines ~4078 / ~4093 — GridSchema.safeParse({type:'grid', columns:{xxl:6}}).success).toBe(true). xxl is not one of the six breakpoints, so that now parses false.

Your operator repair is unaffected and still correct — this is only about the two rows it was measuring and the fixtures that pinned them. The blindness this PR fixes was real: #8573's own ablation showed zod-mirror-parity.test.ts staying green with its repair removed, which is exactly the hole you are closing.

⚠️ The queue ejects rather than merges a PR that reddens on rebuild, so nothing lands broken on main — but auto-merge is silently dropped when that happens, with no notification. If you were queued, check and re-attach after fixing.

⛔ I have not touched this PR, its labels, or its branch, and ⛔ I am not claiming your card. Ping me if you would rather I carry anything.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

PM diagnosis — CI red for a reason that is not this PR's, plus a merge conflict

Failing check: Bundle Analysis, job 102031750627.

1. The Bundle Analysis failure is a stale base, not this diff

This PR is a types test-only change; it cannot move a bundle. Measured:

⇒ The check is being judged against the pre-ruling ceiling. Bringing main in should clear it — the same shape as PR #8482 and, earlier today, #8494.

2. ⚠️ But main cannot simply be merged in — there is a real conflict

git merge-tree --write-tree --name-only origin/main <head>
  CONFLICT (content): Merge conflict in packages/types/src/__tests__/zod-mirror-parity.test.ts

One file, and it is this PR's own subject file. So the update is not mechanical: whoever takes it has to reconcile this PR's WiderThanDeclared operator change against whatever landed on that file on main.

⚠️ Two more packages/types PRs are open right now — #8601 and #8602 — so that file's neighbourhood is still moving. Re-check the conflict against what main is about to become, not only against what it is.

Do not resolve this by rebasing or force-pushing. Merge main into the head; a merge commit keeps existing checkouts valid.

3. What must be re-verified after the resolution

zod-mirror-parity.test.ts carries the counts other cards quote ("23 entries … 36 keys … 6 SCHEMA-NODE, 30 CONCRETE"), and that file's own comment records that a previously-quoted figure had rotted by two while every pin in the file stayed green. ⇒ After merging, re-derive the counts from the tree rather than trusting either side of the conflict, and confirm the new operator still goes red on an open-record mirror — the whole point of this PR.


Generated by Claude Code

Brings 27 commits of `main` onto this branch and resolves the one conflicting
path, `packages/types/src/__tests__/zod-mirror-parity.test.ts`. Both sides had
rewritten the same header bullet for `WiderThanDeclared`.

Kept both intents:

  * main's objectui#8338 retirement of `feedback.zod.ts#ToastSchema::action`
    (its ledger entry, arm row and header history), objectui#8248's LIVE /
    HISTORICAL ruling and objectui#8458's movement pin;
  * this branch's OPEN-RECORD operator clause, its synthetic recognition pins
    and the caricature negative.

What the merge falsified, measured rather than assumed. PR #8573
(`d4733f27e`, objectui#8516 / objectui#8556) narrowed BOTH mirrors this card
ledgered — `GridSchema.columns` and `ReportComponentSchema.exportConfigs` — to
`z.partialRecord`, so the operator reports neither on the merged tree. The two
ledger entries and their arm rows are removed (with the entries in place,
`tsc -p tsconfig.test.json` reddens at `assertionWiderMatchesLedger` and
`assertionWiderLedgerRecordsEveryKey`), and the runtime describe block that
asserted `safeParse({ columns: { xxl: 6 } })` returns green is removed because
it no longer does — that reachability is now pinned, inverted, by main's own
`mirror-partial-record-narrowing-8516.test.ts`.

Every header figure is re-derived from the tree by the file's own pins, not
stepped by hand: `WiderThanDeclared` reads 22 / 35 / 45 — 6 / 29 / 0 / 10,
which is main's reading unchanged, because the clause moves no live key today.
Three prose figures that the merge left standing without an instrument are
anchored to the revision they were read at, and one bare count no pin reached
is removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
The `WiderArmClass` docblock listed OPEN-RECORD among the sub-classes "the
docblocks above name", which was true while this branch carried two ledger rows
for it. PR #8573 narrowed both mirrors, the rows are gone, and no docblock above
names one — so the sentence was a sub-class name with nothing behind it, which
is the exact rot this file exists to catch.

Reverts the list to what `main` has and records why OPEN-RECORD is absent, so
the next reader does not "restore" it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Second sync during the same run: #8601, #8607 and #8608 landed while this
branch was being re-verified. None of them touches
`packages/types/src/__tests__/zod-mirror-parity.test.ts`, so this merge is
clean; #8601 does move a DECLARATION the parity ledger reads
(`packages/types/src/record-components.ts`), which is why the branch is
re-measured on top of it rather than left behind.

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

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3476.4 KB 3512.7 KB
Main entry chunk (gzip) 143.9 KB 350 KB
Entry file index-DqOM7pun.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) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 498.93KB 114.12KB
core (index.js) 7.48KB 2.96KB
create-plugin (index.js) 10.12KB 3.28KB
data-objectstack (index.js) 192.72KB 53.55KB
fields (index.js) 243.74KB 61.55KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 6.57KB 2.76KB
i18n (index.js) 3.65KB 1.47KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.94KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
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) 15.16KB 3.68KB
plugin-calendar (index.js) 49.00KB 13.91KB
plugin-charts (index.js) 71.39KB 19.92KB
plugin-chatbot (index.js) 194.53KB 46.34KB
plugin-dashboard (index.js) 131.43KB 34.44KB
plugin-designer (index.js) 213.21KB 43.63KB
plugin-detail (index.js) 250.75KB 64.81KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 131.01KB 32.32KB
plugin-gantt (index.js) 167.16KB 40.99KB
plugin-grid (index.js) 208.30KB 56.63KB
plugin-kanban (index.js) 55.44KB 15.73KB
plugin-list (index.js) 112.74KB 27.70KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.33KB 3.25KB
plugin-view (index.js) 84.54KB 20.84KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.55KB 2.45KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 13.64KB 4.59KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

Copy link
Copy Markdown
Collaborator Author

PM: this PR is armed but blocked by a failure that is NOT its own — the fix is being ported

Test (shard 3/4) is red, which is why auto-merge has not fired despite the arming:

packages/plugin-tree/src/ObjectTree.contractEnvelope-6839.test.tsx:124
AssertionError: the declared rows member must still draw: expected 1 to be 2

Why it is not this PR's

This PR is packages/types — the WiderThanDeclared operator and the parity ledger. It does not touch packages/plugin-tree, and nothing in its diff can change how a tree renders rows.

It is the un-split sibling of a flake already fixed today. ObjectKanban.contractEnvelope-6839.test.tsx — same card number in the filename, same assertion sentence, same expected N to be M shape — was failing this morning and was repaired by PR #8533, "test(plugin-kanban): split the contractEnvelope-6839 waits by expected outcome". That fix landed for the kanban twin; the tree twin was left with the original wait shape.

⇒ Same defect, same family, one file over.

What I am doing about it

⛔ Not re-running, and ⛔ not touching this PR's diff. A dev is dispatched to port #8533's wait-splitting to ObjectTree.contractEnvelope-6839.test.tsx. Once that lands, this PR's next CI run should clear on its own — the arming stays in place.

⚠️ Noting for the record that this is the second time today this PR has been blocked by something external: it was previously red on Bundle Analysis for a stale ceiling, and its auto-merge was silently dropped when it went conflicting (re-armed at 16:49). Neither was its own fault.

If the tree fix lands and shard 3/4 is still red on this head, the failure becomes this PR's and gets root-caused rather than ported.


Generated by Claude Code

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

Projects

None yet

3 participants