fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8) - #7471

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement
Sep 3, 2026
Merged

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8)#7471
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #6939 — group 6 of 8 (object-map + object-gantt), dispatched as its own PR per the maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8, comment 5510084784). ⛔ Deliberately not a closing keyword: the card stays open until all eight groups land.

The defect

Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName. Both renderers read their records from one of THREE keys, in this order — measured at the read sites, not taken from the card:

  • packages/plugin-map/src/ObjectMap.tsxgetDataConfig (lines 122-167): if (schema.data) … if (schema.staticData) … if (schema.objectName) … return null
  • packages/plugin-gantt/src/ObjectGantt.tsxgetDataConfig (lines 312-331): the same three keys, the same order

No fourth source on either; all three are read by both. A document authored on staticData alone draws correctly and was refused by safeValidateSchema — six catalog entries, three per component, all staticData-only.

The shape

  • objectName is optional on both mirrors and on both TypeScript twins (packages/types/src/objectql.ts), moved in the same stroke.
  • Each member ends in requireRecordSource — a superRefine following the in-repo shape of FormFieldSchema's refinement in form.zod.ts: issue at the ROOT path (no single key is at fault, and blaming objectName would re-teach the requiredness this removes), params.code = 'RECORD_SOURCE_REQUIRED', message naming the three keys an author can supply. Presence is !== undefined — the ruling's wording — not the renderers' truthiness, so the accept set only widens (the changeset carries the argument).
  • object-gantt additionally declares data (ViewDataSchema, the map's spelling) on both faces. It is the FIRST key that resolver reads and was declared on neither face — a refinement over it would have named a key the validator had never heard of. Reported here as the one addition beyond the ruling's literal wording; it is a declared-equals-enforced restoration of the kind the card is about, and the parity ledger is unaffected because both faces gain the same pairing the map pair already carries.
  • Docs: the Schema API blocks of content/docs/plugins/plugin-map.mdx (which still published objectName: string) and plugin-gantt.mdx now say which of the three is required.
  • Changeset: @object-ui/types patch — the accept set only widens toward what already renders.

The pin — both halves the ruling asks for

Validator half (packages/types/src/__tests__/objectql-record-source-refinement-6939.test.ts): the six catalog entries validate (control: each authors no objectName and does author staticData); any one of the three alone validates; objectName: '' still parses (widening); a document with none of the three is refused ON THE REFINEMENT — exactly one issue, root path, RECORD_SOURCE_REQUIRED, all three key names in the message; .shape is still exposed on both objects (zod 4 attaches the check in place); gantt data is declared, not a passthrough hole.

Render half — BEFORE literals measured on origin/maind88e20f55 with the mirrors untouched, through the pin's OWN harness (a SchemaRendererProvider-wrapped bare SchemaRenderer, Date frozen). The harness is named because it matters twice over: a bare SchemaRenderer without the provider renders the error boundary for both components (4 elements, "failed to render") — an identity pin over that is the vacuous pin the ruling warns about — and the docs-gallery harness gives different absolute counts for the same tile.

  • gantt (examples/schema-catalog/test/objectql-record-source-render-identity-6939.test.tsx): 407 / 354 / 436 elements, a tag census, the visible text as a literal, and a SHA-256 of the full textContent (the chart's own style block is part of it). Identical before and after. Anti-vacuity: every authored task title on screen, no error box.
  • map (packages/plugin-map/src/ObjectMap.catalogRecordSource-6939.test.tsx): 16 / 14 / 14 elements, text = the geolocate glyph plus one marker glyph per row, tags in document order, 4 / 3 / 3 markers. Identical before and after. Anti-vacuity: one marker per staticData row, drawn on a LIVE maplibre stand-in (aria-label="Map" control). It lives in plugin-map because the same vi.mock('react-map-gl/maplibre') is measured INERT from examples/schema-catalog (the specifier does not resolve there; the real maplibre mounts and crashes on destroy).

Red-then-green ablation on the committed tree: restore both mirror sources to d88e20f55 (blob hashes checked equal to the base and unequal to HEAD; anchored LINE counts requireRecordSource 5 to 0, TS objectName?: string; 5 to 3, control ObjectMapSchema = BaseSchema.extend 1) → the three pins read 19 failed | 16 passed, exit 1 — every failure validator-facing; every render-identity, anti-vacuity and widening case green. Restore pinned to HEAD and proven by state: git diff HEAD empty, git status --porcelain empty, both blobs equal to HEAD, anchors back to 5 / 5.

Parity ledger

packages/types/src/__tests__/zod-mirror-parity.test.ts is UNTOUCHED. Measured before starting: neither objectql.zod.ts#ObjectMapSchema nor objectql.zod.ts#ObjectGanttSchema has an entry in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared; their only mention is the SPEC_DERIVED_PAIRS list, which is unaffected. Both faces moved together, so the pairs still record zero drift — asserted, not assumed: @object-ui/typestype-check (which compiles tsconfig.test.json) exit 0, and tsc -p tsconfig.test.json --listFiles lists the parity test, the new test and both edited sources (552 files in the program).

Verification (all at afcb85788, heavy runs serialized through the shared verify lock)

  • pnpm exec vitest run packages/types/ packages/plugin-map/ → Test Files 112 passed (112) / Tests 1662 passed (1662)
  • pnpm exec vitest run packages/plugin-gantt/ examples/schema-catalog/ → Test Files 81 passed (81) / Tests 2431 passed (2431)
  • the three pins alone → Test Files 3 passed / Tests 35 passed
  • type-check: @object-ui/types, @object-ui/plugin-map, @object-ui/plugin-gantt, @object-ui/example-schema-catalog — all exit 0 against a freshly built dependency closure (turbo build, 34 tasks)
  • lint for types / plugin-map / example-schema-catalog: 0 errors (267 + 141 pre-existing warnings, none in the new files)
  • gates, each exit 0 on its own verdict line: check:control-bytes, check:doc-fences, check:doc-types, check:doc-snippets (411 of 411 blocks compile), check:spec-symbols, check:vi-mock-specifiers, check:vi-mock-inherit, check:published-tsconfig-exclude, check:phantom-deps, type-check:coverage, changeset-presence, changeset-no-major
  • NOT MEASURED: check:readme-exports exits 1 only because plugin-ai's dist was not part of the built closure (3 unjudgeable self-imports; 403 judged, 0 fabricated); this diff edits no README and adds or removes no export.

Landing

Clause ② yes: this changes what a published mirror accepts. Labelled needs:contract-review, stays DRAFT, waits on the project director seat — not self-reviewed, not enqueued, no auto-merge. That is the expected end state.

Out of scope, filed separately: objectui#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true.

Session session_01EMrWaQw3XS5DxTHxp4yRyC.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…record-source refinement (objectui#6939, group 6 of 8)
Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName, a key
both renderers read THIRD: getDataConfig in plugin-map/src/ObjectMap.tsx and
plugin-gantt/src/ObjectGantt.tsx resolve records from data, then staticData, then
objectName. A document authored on staticData alone drew correctly and was refused
by safeValidateSchema — six catalog entries, three per component.
- objectName becomes optional on both mirrors and both TS twins in the same stroke.
- Each member ends in requireRecordSource: a superRefine whose issue sits at the root
path, carries params.code RECORD_SOURCE_REQUIRED and names data / staticData /
objectName. Presence is `!== undefined` (the ruling's wording), so the accept set
only widens: objectName alone, an empty one included, still parses.
- object-gantt additionally declares `data` (ViewDataSchema, the map's spelling): the
resolver's FIRST read was undeclared on both faces, which would have left the
refinement naming a key the validator had never heard of.
- Pins: validator contract in packages/types (six catalog entries validate; negative
arm checked by path / params.code / message), and render identity in each
renderer's own harness with BEFORE literals measured on d88e20f.
- Docs: the two plugin pages' Schema API blocks say which of the three is required.
Maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3179.2 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRlsZ8mv.js
StatusFAIL

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 halfVerdict
Aggregate closure ceiling✅ pass
Per-chunk ceilings❌ over its ceiling
Ceiling sensitivity (headroom)✅ pass
Ceiling freshness (checkout vs. base branch)✅ pass

📦 Bundle Size Report

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

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Seat bookkeeping — ⛔ NOT a contract review

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Clause ② yes, so this lane implements mechanically and ⛔ does not self-review, enqueue or auto-merge. needs:contract-review is on, the PR is draft, and it waits on the project director seat.

Verified on the branch by content, ⛔ not from the report

probeorigin/mainbranch afcb85788
objectName on the two ruled membersrequired at :584, :670optional at :640, :737, each with a describe naming the three sources
the other six objectName sites in the file66, unchanged
requireRecordSource / RECORD_SOURCE_REQUIREDabsentpresent, applied via .superRefine() on both members
⛔ FENCE — parity ledger, data-display.zod.ts, overlay.zod.ts, complex.zod.tsall four untouched
CONTROL — the diff reaches its targetobjectql.zod.ts +82, objectql.ts +47

The fence row is the one I care most about: five other groups of #6939 and three other in-flight PRs live in those files, and none of them moved.

⭐ The addition beyond the ruling's literal wording — correctly surfaced, and necessary

The ruling says only "objectName becomes optional with a refinement that at least one of data, staticData, objectName is present." The dev found that the gantt faces declared no data at all — the resolver's first read survived purely on BaseSchema's passthrough — and declared it, in the map pair's exact spelling. Measured: gantt data: declarations 2 → 3.

That is not scope creep, it is what makes the ruled refinement coherent: a refusal message naming data as an acceptable source, on a validator that had never heard of data, would tell authors to write a key the schema does not declare. ⇒ Ratified. The dev reported it rather than encoding it silently, which is the part that matters.

Two measurement details worth keeping

The vacuous pin was measured and discarded, not avoided by luck. A bare SchemaRenderer without the provider draws the error boundary for both components — 4 elements, "failed to render". Both tiles look identical before and after under that harness, so the render-identity claim would have passed for the wrong reason. The dev measured it first, threw it away, and states which harness the numbers are from.

A vi.mock was measured INERT.vi.mock('react-map-gl/maplibre') does not resolve from examples/schema-catalog, so real maplibre mounts and crashes; the map half therefore lives in packages/plugin-map. This repo has a check:vi-mock-specifiers gate for exactly that class, and the dev found the instance the gate would not have caught here.

The ablation split is the right shape: 19 red / 16 green, with every failure validator-facing and every render-identity, anti-vacuity and widening case green. Restore proven by state.

Filed, not acted on

#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true (plugin-map/src/index.tsx:67,77, plugin-gantt/src/index.tsx:107,117, restated in plugin-gantt/README.md:133) while the renderers read three record sources. Registrations were outside the ruling and are left as they stand. ⇒ After this PR the mirror and the designer input disagree; that is the follow-up's to reconcile, not this PR's to widen.

⚠️Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling. Measurement and path proof: #7399 comments 5522186641 / 5522268764, PR #7467 comment 5522177212.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ Correction to my own comment above — I called the Bundle Analysis red "not this PR's" without the proof, and this PR is not exempt

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Retracting one clause of 5522300143.

What I wrote:"Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling."

Why that was an overreach: the framework chunk is attributed by path/[\\/]packages[\\/](core|react|types)[\\/]/. That argument worked for PR #7467 because its entire file list is plugin-timeline and i18n, so it structurally cannot add a byte to the chunk. This PR edits packages/types/src/objectql.ts (+47) and packages/types/src/zod/objectql.zod.ts (+82) — squarely inside the group. I reused #7467's conclusion on a PR whose premise does not hold, and I did it one message after writing that "any not mine claim still needs the path-list proof."

The measurement, now read from the job log

framework 511.9 KB / 511.7 KB ceiling (OVER by 0.2 KB)
EAGER_CLOSURE_BASE_SHA: 0246d116e ← a NEWER main than #7467's c1fe272ad

Against #7467's reading on c1fe272ad512.8 KB, over by 1.1 KB — two things moved at once:

  1. main improved. Something landed between c1fe272ad and 0246d116e that took roughly 0.9 KB back out of the chunk. main is no longer 1.1 KB over.
  2. This PR adds packages/types bytes on top of whatever main now carries, and I cannot separate the two from this run alone.

Unresolved, not exonerated. The honest statement is: main at 0246d116e may or may not still be over on its own, and this PR's +129 lines of packages/types may or may not be what carries it past 511.7. Deciding that needs a main-alone measurement at 0246d116e, which did not exist when I wrote the earlier comment and does not exist yet.

I have redirected the running measurement to take it. Until it reports:

  • ⛔ This PR is not cleared of its byte cost, and my earlier sentence should not be read as clearing it.
  • ⛔ Still no ceiling raise, and still no re-run — the build is deterministic.
  • The PR is Clause ② yes and draft regardless, so nothing was going to land on the strength of that sentence. The correction matters for the record, not for an action I took.

⭐ The general rule this violates is the one I had just written into my own check-in: while main is over, the per-chunk verdict carries no per-PR information, so every "not mine" claim needs its own path-list proof. A PR that edits packages/types never had one available.

Everything else in 5522300143 — the by-content verification, the ratified data declaration, the fence readings — stands unchanged.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS (content); landing blocked by the per-chunk eager-closure ceiling, not by this diff's correctness

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head afcb85788. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6939 ruling row for this group (recorded 2026-09-02, batch #8): objectName optional on both mirrors and both TypeScript twins; each member ends in requireRecordSource, root-path issue with params.code = 'RECORD_SOURCE_REQUIRED', presence by !== undefined (the ruling's wording, not the renderers' truthiness), so the accept set only widens. The one addition beyond the ruling's literal text — object-gantt declaring data — is the right call and is reported: it is the FIRST key the resolver reads and was undeclared on both faces, so a refinement naming it would otherwise have referenced a key the validator had never heard of; it is spelled exactly as the map's. The pin carries both halves: six catalog entries validate; render identity measured on d88e20f55 through the pins' own harness (the vacuous-harness trap is named and avoided), anti-vacuity on both; the gantt data counter-probe shows it is declared, not a passthrough hole. .shape survives the refinement (zod 4 in-place), so the parity census is unaffected, measured.

Semver / changeset.@object-ui/typespatch: widening only — objectName alone, empty included, still parses; the one refused shape was refused before. Correct.

Boundary flags. No governed path; plugin-map/tsconfig.test.json gains node types for the fixture read (the shape two sibling packages already use).

Landing — blocked, and not by this PR. Every check is green except Bundle Analysis: BUDGET_CLOSURE_STATUS: pass (3179.2 KB against 3191.4 KB), BUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399 ("the framework per-chunk ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land"). This PR is now the third such fix. The director seat presents #7399 to the maintainer in the next decision batch; until it is ruled this PR stays draft with its carrier cleared (the review is complete), and the objectui seat re-runs Bundle Analysis after the ruling lands rather than shaving bytes to fit under a ceiling whose size is the open question.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3180.0 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-DMoCUVyD.js
StatusPASS

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


📦 Bundle Size Report

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

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:08
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Landing — the ceiling this was parked behind is fixed. Contract PASS stands; ready + auto-merge.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance for the release action: the in-seat contract-review route (维护者 2026-08-31 裁定 —— 交付后复核由派发席在席内完成,PASS ⇒ 同席剥标、ready、auto-merge).

The blocker named in the review is gone

The contract review (5522971250) returned PASS on content and blocked landing on one thing only:

Every check is green except Bundle AnalysisBUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399.

#7399 was ruled (maintainer, 2026-09-03, option A′) and landed as 177afeba1 (PR #7489). The framework chunk was 78.7 % @object-ui/i18n locale catalogueframework and infrastructure tied at priority 80 and framework was written first — so the ceiling that rejected this PR was never budgeting core|react|types at all. The catalogue now has its own i18n-locales chunk and both ceilings were re-baselined in the same change.

main was merged in, and Bundle Analysis on 29e117bd is now ✅ PASS (3180.0 KB / 3191.4 KB, 50 chunks) — 5524564830. ⛔ Not one byte of this PR's own diff was changed to achieve it, no ceiling was raised, no test touched.

The three pre-landing conditions, checked rather than assumed

#conditionreading
in-seat contract PASS on record5522971250 — director seat, at CONTRACT_REVIEW_TIER, fuse read claude-fable-5-1 against claude-fable-5
needs:contract-review cleared on both carriers✅ by hand — PR labels package: types / plugin / examples / tests; card #6939 labels pm:dispatched / priority:p2 / domain:ui. ⚠️ see the caveat below
all checks green by name (⛔ not the required subset)✅ 32/32 — 27 success, 5 skipped (dependabot, the two coverage jobs, …). Bundle Analysis, Type Check, Lint, all four Test (shard N/4), Governed Surface Queue Guard each read individually

⭐ Why re-review was not required, established mechanically rather than assumed

The review was performed at head afcb85788; the head is now 29e117bd. A moved head normally means the gate label goes back on. It does not here, and this was checked, not asserted: the second commit is a pure Merge branch 'main', and

git diff --stat afcb85788 29e117bd -- <the 9 files this branch changes> → empty

⇒ the reviewed contract increment is byte-identical at the landing head. The merge resolved nothing in this branch's own files.

⚠️ Recorded against the clean reading of ②

node scripts/pm/check-clause2-carriers.mjs --pair 7471 is the machine check for the dual-carrier condition. It could not run in this session — it exits 2 with "the pair could not be formed, so nothing about it was judged", because this seat reaches GitHub only through MCP and the script has no token of its own. Its own output says that is ⛔ not a clearance, so I am not claiming a machine reading: ② above is a manual verification of both label sets. Flagging it because a hand check is the weaker instrument and the next seat should know which one was used.

⚠️ And one correction against myself

I had this PR recorded as "at the director seat, Clause ② yes — do not chase", and left it sitting on that basis. That was wrong. The rule is 「交付后复核由派发席在席内完成……PASS ⇒ 同席剥标、ready、auto-merge」 — the director seat's role here is optional post-hoc audit, not the release action. The review had been complete and passing since 08:36; the PR then waited on my misreading rather than on anything real. It also means I nearly commissioned a duplicate contract review of an already-passed increment, which I stopped once I read this thread.

Part of #6939 is deliberately not a closing keyword — the card stays open until all eight groups land. Group 6 of 8.


Generated by Claude Code

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.

2 participants

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

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8) - #7471

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement
Sep 3, 2026
Merged

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8)#7471
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #6939 — group 6 of 8 (object-map + object-gantt), dispatched as its own PR per the maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8, comment 5510084784). ⛔ Deliberately not a closing keyword: the card stays open until all eight groups land.

The defect

Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName. Both renderers read their records from one of THREE keys, in this order — measured at the read sites, not taken from the card:

  • packages/plugin-map/src/ObjectMap.tsxgetDataConfig (lines 122-167): if (schema.data) … if (schema.staticData) … if (schema.objectName) … return null
  • packages/plugin-gantt/src/ObjectGantt.tsxgetDataConfig (lines 312-331): the same three keys, the same order

No fourth source on either; all three are read by both. A document authored on staticData alone draws correctly and was refused by safeValidateSchema — six catalog entries, three per component, all staticData-only.

The shape

  • objectName is optional on both mirrors and on both TypeScript twins (packages/types/src/objectql.ts), moved in the same stroke.
  • Each member ends in requireRecordSource — a superRefine following the in-repo shape of FormFieldSchema's refinement in form.zod.ts: issue at the ROOT path (no single key is at fault, and blaming objectName would re-teach the requiredness this removes), params.code = 'RECORD_SOURCE_REQUIRED', message naming the three keys an author can supply. Presence is !== undefined — the ruling's wording — not the renderers' truthiness, so the accept set only widens (the changeset carries the argument).
  • object-gantt additionally declares data (ViewDataSchema, the map's spelling) on both faces. It is the FIRST key that resolver reads and was declared on neither face — a refinement over it would have named a key the validator had never heard of. Reported here as the one addition beyond the ruling's literal wording; it is a declared-equals-enforced restoration of the kind the card is about, and the parity ledger is unaffected because both faces gain the same pairing the map pair already carries.
  • Docs: the Schema API blocks of content/docs/plugins/plugin-map.mdx (which still published objectName: string) and plugin-gantt.mdx now say which of the three is required.
  • Changeset: @object-ui/types patch — the accept set only widens toward what already renders.

The pin — both halves the ruling asks for

Validator half (packages/types/src/__tests__/objectql-record-source-refinement-6939.test.ts): the six catalog entries validate (control: each authors no objectName and does author staticData); any one of the three alone validates; objectName: '' still parses (widening); a document with none of the three is refused ON THE REFINEMENT — exactly one issue, root path, RECORD_SOURCE_REQUIRED, all three key names in the message; .shape is still exposed on both objects (zod 4 attaches the check in place); gantt data is declared, not a passthrough hole.

Render half — BEFORE literals measured on origin/maind88e20f55 with the mirrors untouched, through the pin's OWN harness (a SchemaRendererProvider-wrapped bare SchemaRenderer, Date frozen). The harness is named because it matters twice over: a bare SchemaRenderer without the provider renders the error boundary for both components (4 elements, "failed to render") — an identity pin over that is the vacuous pin the ruling warns about — and the docs-gallery harness gives different absolute counts for the same tile.

  • gantt (examples/schema-catalog/test/objectql-record-source-render-identity-6939.test.tsx): 407 / 354 / 436 elements, a tag census, the visible text as a literal, and a SHA-256 of the full textContent (the chart's own style block is part of it). Identical before and after. Anti-vacuity: every authored task title on screen, no error box.
  • map (packages/plugin-map/src/ObjectMap.catalogRecordSource-6939.test.tsx): 16 / 14 / 14 elements, text = the geolocate glyph plus one marker glyph per row, tags in document order, 4 / 3 / 3 markers. Identical before and after. Anti-vacuity: one marker per staticData row, drawn on a LIVE maplibre stand-in (aria-label="Map" control). It lives in plugin-map because the same vi.mock('react-map-gl/maplibre') is measured INERT from examples/schema-catalog (the specifier does not resolve there; the real maplibre mounts and crashes on destroy).

Red-then-green ablation on the committed tree: restore both mirror sources to d88e20f55 (blob hashes checked equal to the base and unequal to HEAD; anchored LINE counts requireRecordSource 5 to 0, TS objectName?: string; 5 to 3, control ObjectMapSchema = BaseSchema.extend 1) → the three pins read 19 failed | 16 passed, exit 1 — every failure validator-facing; every render-identity, anti-vacuity and widening case green. Restore pinned to HEAD and proven by state: git diff HEAD empty, git status --porcelain empty, both blobs equal to HEAD, anchors back to 5 / 5.

Parity ledger

packages/types/src/__tests__/zod-mirror-parity.test.ts is UNTOUCHED. Measured before starting: neither objectql.zod.ts#ObjectMapSchema nor objectql.zod.ts#ObjectGanttSchema has an entry in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared; their only mention is the SPEC_DERIVED_PAIRS list, which is unaffected. Both faces moved together, so the pairs still record zero drift — asserted, not assumed: @object-ui/typestype-check (which compiles tsconfig.test.json) exit 0, and tsc -p tsconfig.test.json --listFiles lists the parity test, the new test and both edited sources (552 files in the program).

Verification (all at afcb85788, heavy runs serialized through the shared verify lock)

  • pnpm exec vitest run packages/types/ packages/plugin-map/ → Test Files 112 passed (112) / Tests 1662 passed (1662)
  • pnpm exec vitest run packages/plugin-gantt/ examples/schema-catalog/ → Test Files 81 passed (81) / Tests 2431 passed (2431)
  • the three pins alone → Test Files 3 passed / Tests 35 passed
  • type-check: @object-ui/types, @object-ui/plugin-map, @object-ui/plugin-gantt, @object-ui/example-schema-catalog — all exit 0 against a freshly built dependency closure (turbo build, 34 tasks)
  • lint for types / plugin-map / example-schema-catalog: 0 errors (267 + 141 pre-existing warnings, none in the new files)
  • gates, each exit 0 on its own verdict line: check:control-bytes, check:doc-fences, check:doc-types, check:doc-snippets (411 of 411 blocks compile), check:spec-symbols, check:vi-mock-specifiers, check:vi-mock-inherit, check:published-tsconfig-exclude, check:phantom-deps, type-check:coverage, changeset-presence, changeset-no-major
  • NOT MEASURED: check:readme-exports exits 1 only because plugin-ai's dist was not part of the built closure (3 unjudgeable self-imports; 403 judged, 0 fabricated); this diff edits no README and adds or removes no export.

Landing

Clause ② yes: this changes what a published mirror accepts. Labelled needs:contract-review, stays DRAFT, waits on the project director seat — not self-reviewed, not enqueued, no auto-merge. That is the expected end state.

Out of scope, filed separately: objectui#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true.

Session session_01EMrWaQw3XS5DxTHxp4yRyC.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…record-source refinement (objectui#6939, group 6 of 8)
Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName, a key
both renderers read THIRD: getDataConfig in plugin-map/src/ObjectMap.tsx and
plugin-gantt/src/ObjectGantt.tsx resolve records from data, then staticData, then
objectName. A document authored on staticData alone drew correctly and was refused
by safeValidateSchema — six catalog entries, three per component.
- objectName becomes optional on both mirrors and both TS twins in the same stroke.
- Each member ends in requireRecordSource: a superRefine whose issue sits at the root
path, carries params.code RECORD_SOURCE_REQUIRED and names data / staticData /
objectName. Presence is `!== undefined` (the ruling's wording), so the accept set
only widens: objectName alone, an empty one included, still parses.
- object-gantt additionally declares `data` (ViewDataSchema, the map's spelling): the
resolver's FIRST read was undeclared on both faces, which would have left the
refinement naming a key the validator had never heard of.
- Pins: validator contract in packages/types (six catalog entries validate; negative
arm checked by path / params.code / message), and render identity in each
renderer's own harness with BEFORE literals measured on d88e20f.
- Docs: the two plugin pages' Schema API blocks say which of the three is required.
Maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3179.2 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRlsZ8mv.js
StatusFAIL

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 halfVerdict
Aggregate closure ceiling✅ pass
Per-chunk ceilings❌ over its ceiling
Ceiling sensitivity (headroom)✅ pass
Ceiling freshness (checkout vs. base branch)✅ pass

📦 Bundle Size Report

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

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Seat bookkeeping — ⛔ NOT a contract review

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Clause ② yes, so this lane implements mechanically and ⛔ does not self-review, enqueue or auto-merge. needs:contract-review is on, the PR is draft, and it waits on the project director seat.

Verified on the branch by content, ⛔ not from the report

probeorigin/mainbranch afcb85788
objectName on the two ruled membersrequired at :584, :670optional at :640, :737, each with a describe naming the three sources
the other six objectName sites in the file66, unchanged
requireRecordSource / RECORD_SOURCE_REQUIREDabsentpresent, applied via .superRefine() on both members
⛔ FENCE — parity ledger, data-display.zod.ts, overlay.zod.ts, complex.zod.tsall four untouched
CONTROL — the diff reaches its targetobjectql.zod.ts +82, objectql.ts +47

The fence row is the one I care most about: five other groups of #6939 and three other in-flight PRs live in those files, and none of them moved.

⭐ The addition beyond the ruling's literal wording — correctly surfaced, and necessary

The ruling says only "objectName becomes optional with a refinement that at least one of data, staticData, objectName is present." The dev found that the gantt faces declared no data at all — the resolver's first read survived purely on BaseSchema's passthrough — and declared it, in the map pair's exact spelling. Measured: gantt data: declarations 2 → 3.

That is not scope creep, it is what makes the ruled refinement coherent: a refusal message naming data as an acceptable source, on a validator that had never heard of data, would tell authors to write a key the schema does not declare. ⇒ Ratified. The dev reported it rather than encoding it silently, which is the part that matters.

Two measurement details worth keeping

The vacuous pin was measured and discarded, not avoided by luck. A bare SchemaRenderer without the provider draws the error boundary for both components — 4 elements, "failed to render". Both tiles look identical before and after under that harness, so the render-identity claim would have passed for the wrong reason. The dev measured it first, threw it away, and states which harness the numbers are from.

A vi.mock was measured INERT.vi.mock('react-map-gl/maplibre') does not resolve from examples/schema-catalog, so real maplibre mounts and crashes; the map half therefore lives in packages/plugin-map. This repo has a check:vi-mock-specifiers gate for exactly that class, and the dev found the instance the gate would not have caught here.

The ablation split is the right shape: 19 red / 16 green, with every failure validator-facing and every render-identity, anti-vacuity and widening case green. Restore proven by state.

Filed, not acted on

#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true (plugin-map/src/index.tsx:67,77, plugin-gantt/src/index.tsx:107,117, restated in plugin-gantt/README.md:133) while the renderers read three record sources. Registrations were outside the ruling and are left as they stand. ⇒ After this PR the mirror and the designer input disagree; that is the follow-up's to reconcile, not this PR's to widen.

⚠️Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling. Measurement and path proof: #7399 comments 5522186641 / 5522268764, PR #7467 comment 5522177212.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ Correction to my own comment above — I called the Bundle Analysis red "not this PR's" without the proof, and this PR is not exempt

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Retracting one clause of 5522300143.

What I wrote:"Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling."

Why that was an overreach: the framework chunk is attributed by path/[\\/]packages[\\/](core|react|types)[\\/]/. That argument worked for PR #7467 because its entire file list is plugin-timeline and i18n, so it structurally cannot add a byte to the chunk. This PR edits packages/types/src/objectql.ts (+47) and packages/types/src/zod/objectql.zod.ts (+82) — squarely inside the group. I reused #7467's conclusion on a PR whose premise does not hold, and I did it one message after writing that "any not mine claim still needs the path-list proof."

The measurement, now read from the job log

framework 511.9 KB / 511.7 KB ceiling (OVER by 0.2 KB)
EAGER_CLOSURE_BASE_SHA: 0246d116e ← a NEWER main than #7467's c1fe272ad

Against #7467's reading on c1fe272ad512.8 KB, over by 1.1 KB — two things moved at once:

  1. main improved. Something landed between c1fe272ad and 0246d116e that took roughly 0.9 KB back out of the chunk. main is no longer 1.1 KB over.
  2. This PR adds packages/types bytes on top of whatever main now carries, and I cannot separate the two from this run alone.

Unresolved, not exonerated. The honest statement is: main at 0246d116e may or may not still be over on its own, and this PR's +129 lines of packages/types may or may not be what carries it past 511.7. Deciding that needs a main-alone measurement at 0246d116e, which did not exist when I wrote the earlier comment and does not exist yet.

I have redirected the running measurement to take it. Until it reports:

  • ⛔ This PR is not cleared of its byte cost, and my earlier sentence should not be read as clearing it.
  • ⛔ Still no ceiling raise, and still no re-run — the build is deterministic.
  • The PR is Clause ② yes and draft regardless, so nothing was going to land on the strength of that sentence. The correction matters for the record, not for an action I took.

⭐ The general rule this violates is the one I had just written into my own check-in: while main is over, the per-chunk verdict carries no per-PR information, so every "not mine" claim needs its own path-list proof. A PR that edits packages/types never had one available.

Everything else in 5522300143 — the by-content verification, the ratified data declaration, the fence readings — stands unchanged.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS (content); landing blocked by the per-chunk eager-closure ceiling, not by this diff's correctness

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head afcb85788. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6939 ruling row for this group (recorded 2026-09-02, batch #8): objectName optional on both mirrors and both TypeScript twins; each member ends in requireRecordSource, root-path issue with params.code = 'RECORD_SOURCE_REQUIRED', presence by !== undefined (the ruling's wording, not the renderers' truthiness), so the accept set only widens. The one addition beyond the ruling's literal text — object-gantt declaring data — is the right call and is reported: it is the FIRST key the resolver reads and was undeclared on both faces, so a refinement naming it would otherwise have referenced a key the validator had never heard of; it is spelled exactly as the map's. The pin carries both halves: six catalog entries validate; render identity measured on d88e20f55 through the pins' own harness (the vacuous-harness trap is named and avoided), anti-vacuity on both; the gantt data counter-probe shows it is declared, not a passthrough hole. .shape survives the refinement (zod 4 in-place), so the parity census is unaffected, measured.

Semver / changeset.@object-ui/typespatch: widening only — objectName alone, empty included, still parses; the one refused shape was refused before. Correct.

Boundary flags. No governed path; plugin-map/tsconfig.test.json gains node types for the fixture read (the shape two sibling packages already use).

Landing — blocked, and not by this PR. Every check is green except Bundle Analysis: BUDGET_CLOSURE_STATUS: pass (3179.2 KB against 3191.4 KB), BUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399 ("the framework per-chunk ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land"). This PR is now the third such fix. The director seat presents #7399 to the maintainer in the next decision batch; until it is ruled this PR stays draft with its carrier cleared (the review is complete), and the objectui seat re-runs Bundle Analysis after the ruling lands rather than shaving bytes to fit under a ceiling whose size is the open question.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3180.0 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-DMoCUVyD.js
StatusPASS

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


📦 Bundle Size Report

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

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:08
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Landing — the ceiling this was parked behind is fixed. Contract PASS stands; ready + auto-merge.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance for the release action: the in-seat contract-review route (维护者 2026-08-31 裁定 —— 交付后复核由派发席在席内完成,PASS ⇒ 同席剥标、ready、auto-merge).

The blocker named in the review is gone

The contract review (5522971250) returned PASS on content and blocked landing on one thing only:

Every check is green except Bundle AnalysisBUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399.

#7399 was ruled (maintainer, 2026-09-03, option A′) and landed as 177afeba1 (PR #7489). The framework chunk was 78.7 % @object-ui/i18n locale catalogueframework and infrastructure tied at priority 80 and framework was written first — so the ceiling that rejected this PR was never budgeting core|react|types at all. The catalogue now has its own i18n-locales chunk and both ceilings were re-baselined in the same change.

main was merged in, and Bundle Analysis on 29e117bd is now ✅ PASS (3180.0 KB / 3191.4 KB, 50 chunks) — 5524564830. ⛔ Not one byte of this PR's own diff was changed to achieve it, no ceiling was raised, no test touched.

The three pre-landing conditions, checked rather than assumed

#conditionreading
in-seat contract PASS on record5522971250 — director seat, at CONTRACT_REVIEW_TIER, fuse read claude-fable-5-1 against claude-fable-5
needs:contract-review cleared on both carriers✅ by hand — PR labels package: types / plugin / examples / tests; card #6939 labels pm:dispatched / priority:p2 / domain:ui. ⚠️ see the caveat below
all checks green by name (⛔ not the required subset)✅ 32/32 — 27 success, 5 skipped (dependabot, the two coverage jobs, …). Bundle Analysis, Type Check, Lint, all four Test (shard N/4), Governed Surface Queue Guard each read individually

⭐ Why re-review was not required, established mechanically rather than assumed

The review was performed at head afcb85788; the head is now 29e117bd. A moved head normally means the gate label goes back on. It does not here, and this was checked, not asserted: the second commit is a pure Merge branch 'main', and

git diff --stat afcb85788 29e117bd -- <the 9 files this branch changes> → empty

⇒ the reviewed contract increment is byte-identical at the landing head. The merge resolved nothing in this branch's own files.

⚠️ Recorded against the clean reading of ②

node scripts/pm/check-clause2-carriers.mjs --pair 7471 is the machine check for the dual-carrier condition. It could not run in this session — it exits 2 with "the pair could not be formed, so nothing about it was judged", because this seat reaches GitHub only through MCP and the script has no token of its own. Its own output says that is ⛔ not a clearance, so I am not claiming a machine reading: ② above is a manual verification of both label sets. Flagging it because a hand check is the weaker instrument and the next seat should know which one was used.

⚠️ And one correction against myself

I had this PR recorded as "at the director seat, Clause ② yes — do not chase", and left it sitting on that basis. That was wrong. The rule is 「交付后复核由派发席在席内完成……PASS ⇒ 同席剥标、ready、auto-merge」 — the director seat's role here is optional post-hoc audit, not the release action. The review had been complete and passing since 08:36; the PR then waited on my misreading rather than on anything real. It also means I nearly commissioned a duplicate contract review of an already-passed increment, which I stopped once I read this thread.

Part of #6939 is deliberately not a closing keyword — the card stays open until all eight groups land. Group 6 of 8.


Generated by Claude Code

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.

2 participants

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

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8) - #7471

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement
Sep 3, 2026
Merged

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8)#7471
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #6939 — group 6 of 8 (object-map + object-gantt), dispatched as its own PR per the maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8, comment 5510084784). ⛔ Deliberately not a closing keyword: the card stays open until all eight groups land.

The defect

Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName. Both renderers read their records from one of THREE keys, in this order — measured at the read sites, not taken from the card:

  • packages/plugin-map/src/ObjectMap.tsxgetDataConfig (lines 122-167): if (schema.data) … if (schema.staticData) … if (schema.objectName) … return null
  • packages/plugin-gantt/src/ObjectGantt.tsxgetDataConfig (lines 312-331): the same three keys, the same order

No fourth source on either; all three are read by both. A document authored on staticData alone draws correctly and was refused by safeValidateSchema — six catalog entries, three per component, all staticData-only.

The shape

  • objectName is optional on both mirrors and on both TypeScript twins (packages/types/src/objectql.ts), moved in the same stroke.
  • Each member ends in requireRecordSource — a superRefine following the in-repo shape of FormFieldSchema's refinement in form.zod.ts: issue at the ROOT path (no single key is at fault, and blaming objectName would re-teach the requiredness this removes), params.code = 'RECORD_SOURCE_REQUIRED', message naming the three keys an author can supply. Presence is !== undefined — the ruling's wording — not the renderers' truthiness, so the accept set only widens (the changeset carries the argument).
  • object-gantt additionally declares data (ViewDataSchema, the map's spelling) on both faces. It is the FIRST key that resolver reads and was declared on neither face — a refinement over it would have named a key the validator had never heard of. Reported here as the one addition beyond the ruling's literal wording; it is a declared-equals-enforced restoration of the kind the card is about, and the parity ledger is unaffected because both faces gain the same pairing the map pair already carries.
  • Docs: the Schema API blocks of content/docs/plugins/plugin-map.mdx (which still published objectName: string) and plugin-gantt.mdx now say which of the three is required.
  • Changeset: @object-ui/types patch — the accept set only widens toward what already renders.

The pin — both halves the ruling asks for

Validator half (packages/types/src/__tests__/objectql-record-source-refinement-6939.test.ts): the six catalog entries validate (control: each authors no objectName and does author staticData); any one of the three alone validates; objectName: '' still parses (widening); a document with none of the three is refused ON THE REFINEMENT — exactly one issue, root path, RECORD_SOURCE_REQUIRED, all three key names in the message; .shape is still exposed on both objects (zod 4 attaches the check in place); gantt data is declared, not a passthrough hole.

Render half — BEFORE literals measured on origin/maind88e20f55 with the mirrors untouched, through the pin's OWN harness (a SchemaRendererProvider-wrapped bare SchemaRenderer, Date frozen). The harness is named because it matters twice over: a bare SchemaRenderer without the provider renders the error boundary for both components (4 elements, "failed to render") — an identity pin over that is the vacuous pin the ruling warns about — and the docs-gallery harness gives different absolute counts for the same tile.

  • gantt (examples/schema-catalog/test/objectql-record-source-render-identity-6939.test.tsx): 407 / 354 / 436 elements, a tag census, the visible text as a literal, and a SHA-256 of the full textContent (the chart's own style block is part of it). Identical before and after. Anti-vacuity: every authored task title on screen, no error box.
  • map (packages/plugin-map/src/ObjectMap.catalogRecordSource-6939.test.tsx): 16 / 14 / 14 elements, text = the geolocate glyph plus one marker glyph per row, tags in document order, 4 / 3 / 3 markers. Identical before and after. Anti-vacuity: one marker per staticData row, drawn on a LIVE maplibre stand-in (aria-label="Map" control). It lives in plugin-map because the same vi.mock('react-map-gl/maplibre') is measured INERT from examples/schema-catalog (the specifier does not resolve there; the real maplibre mounts and crashes on destroy).

Red-then-green ablation on the committed tree: restore both mirror sources to d88e20f55 (blob hashes checked equal to the base and unequal to HEAD; anchored LINE counts requireRecordSource 5 to 0, TS objectName?: string; 5 to 3, control ObjectMapSchema = BaseSchema.extend 1) → the three pins read 19 failed | 16 passed, exit 1 — every failure validator-facing; every render-identity, anti-vacuity and widening case green. Restore pinned to HEAD and proven by state: git diff HEAD empty, git status --porcelain empty, both blobs equal to HEAD, anchors back to 5 / 5.

Parity ledger

packages/types/src/__tests__/zod-mirror-parity.test.ts is UNTOUCHED. Measured before starting: neither objectql.zod.ts#ObjectMapSchema nor objectql.zod.ts#ObjectGanttSchema has an entry in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared; their only mention is the SPEC_DERIVED_PAIRS list, which is unaffected. Both faces moved together, so the pairs still record zero drift — asserted, not assumed: @object-ui/typestype-check (which compiles tsconfig.test.json) exit 0, and tsc -p tsconfig.test.json --listFiles lists the parity test, the new test and both edited sources (552 files in the program).

Verification (all at afcb85788, heavy runs serialized through the shared verify lock)

  • pnpm exec vitest run packages/types/ packages/plugin-map/ → Test Files 112 passed (112) / Tests 1662 passed (1662)
  • pnpm exec vitest run packages/plugin-gantt/ examples/schema-catalog/ → Test Files 81 passed (81) / Tests 2431 passed (2431)
  • the three pins alone → Test Files 3 passed / Tests 35 passed
  • type-check: @object-ui/types, @object-ui/plugin-map, @object-ui/plugin-gantt, @object-ui/example-schema-catalog — all exit 0 against a freshly built dependency closure (turbo build, 34 tasks)
  • lint for types / plugin-map / example-schema-catalog: 0 errors (267 + 141 pre-existing warnings, none in the new files)
  • gates, each exit 0 on its own verdict line: check:control-bytes, check:doc-fences, check:doc-types, check:doc-snippets (411 of 411 blocks compile), check:spec-symbols, check:vi-mock-specifiers, check:vi-mock-inherit, check:published-tsconfig-exclude, check:phantom-deps, type-check:coverage, changeset-presence, changeset-no-major
  • NOT MEASURED: check:readme-exports exits 1 only because plugin-ai's dist was not part of the built closure (3 unjudgeable self-imports; 403 judged, 0 fabricated); this diff edits no README and adds or removes no export.

Landing

Clause ② yes: this changes what a published mirror accepts. Labelled needs:contract-review, stays DRAFT, waits on the project director seat — not self-reviewed, not enqueued, no auto-merge. That is the expected end state.

Out of scope, filed separately: objectui#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true.

Session session_01EMrWaQw3XS5DxTHxp4yRyC.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…record-source refinement (objectui#6939, group 6 of 8)
Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName, a key
both renderers read THIRD: getDataConfig in plugin-map/src/ObjectMap.tsx and
plugin-gantt/src/ObjectGantt.tsx resolve records from data, then staticData, then
objectName. A document authored on staticData alone drew correctly and was refused
by safeValidateSchema — six catalog entries, three per component.
- objectName becomes optional on both mirrors and both TS twins in the same stroke.
- Each member ends in requireRecordSource: a superRefine whose issue sits at the root
path, carries params.code RECORD_SOURCE_REQUIRED and names data / staticData /
objectName. Presence is `!== undefined` (the ruling's wording), so the accept set
only widens: objectName alone, an empty one included, still parses.
- object-gantt additionally declares `data` (ViewDataSchema, the map's spelling): the
resolver's FIRST read was undeclared on both faces, which would have left the
refinement naming a key the validator had never heard of.
- Pins: validator contract in packages/types (six catalog entries validate; negative
arm checked by path / params.code / message), and render identity in each
renderer's own harness with BEFORE literals measured on d88e20f.
- Docs: the two plugin pages' Schema API blocks say which of the three is required.
Maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3179.2 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRlsZ8mv.js
StatusFAIL

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 halfVerdict
Aggregate closure ceiling✅ pass
Per-chunk ceilings❌ over its ceiling
Ceiling sensitivity (headroom)✅ pass
Ceiling freshness (checkout vs. base branch)✅ pass

📦 Bundle Size Report

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

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Seat bookkeeping — ⛔ NOT a contract review

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Clause ② yes, so this lane implements mechanically and ⛔ does not self-review, enqueue or auto-merge. needs:contract-review is on, the PR is draft, and it waits on the project director seat.

Verified on the branch by content, ⛔ not from the report

probeorigin/mainbranch afcb85788
objectName on the two ruled membersrequired at :584, :670optional at :640, :737, each with a describe naming the three sources
the other six objectName sites in the file66, unchanged
requireRecordSource / RECORD_SOURCE_REQUIREDabsentpresent, applied via .superRefine() on both members
⛔ FENCE — parity ledger, data-display.zod.ts, overlay.zod.ts, complex.zod.tsall four untouched
CONTROL — the diff reaches its targetobjectql.zod.ts +82, objectql.ts +47

The fence row is the one I care most about: five other groups of #6939 and three other in-flight PRs live in those files, and none of them moved.

⭐ The addition beyond the ruling's literal wording — correctly surfaced, and necessary

The ruling says only "objectName becomes optional with a refinement that at least one of data, staticData, objectName is present." The dev found that the gantt faces declared no data at all — the resolver's first read survived purely on BaseSchema's passthrough — and declared it, in the map pair's exact spelling. Measured: gantt data: declarations 2 → 3.

That is not scope creep, it is what makes the ruled refinement coherent: a refusal message naming data as an acceptable source, on a validator that had never heard of data, would tell authors to write a key the schema does not declare. ⇒ Ratified. The dev reported it rather than encoding it silently, which is the part that matters.

Two measurement details worth keeping

The vacuous pin was measured and discarded, not avoided by luck. A bare SchemaRenderer without the provider draws the error boundary for both components — 4 elements, "failed to render". Both tiles look identical before and after under that harness, so the render-identity claim would have passed for the wrong reason. The dev measured it first, threw it away, and states which harness the numbers are from.

A vi.mock was measured INERT.vi.mock('react-map-gl/maplibre') does not resolve from examples/schema-catalog, so real maplibre mounts and crashes; the map half therefore lives in packages/plugin-map. This repo has a check:vi-mock-specifiers gate for exactly that class, and the dev found the instance the gate would not have caught here.

The ablation split is the right shape: 19 red / 16 green, with every failure validator-facing and every render-identity, anti-vacuity and widening case green. Restore proven by state.

Filed, not acted on

#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true (plugin-map/src/index.tsx:67,77, plugin-gantt/src/index.tsx:107,117, restated in plugin-gantt/README.md:133) while the renderers read three record sources. Registrations were outside the ruling and are left as they stand. ⇒ After this PR the mirror and the designer input disagree; that is the follow-up's to reconcile, not this PR's to widen.

⚠️Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling. Measurement and path proof: #7399 comments 5522186641 / 5522268764, PR #7467 comment 5522177212.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ Correction to my own comment above — I called the Bundle Analysis red "not this PR's" without the proof, and this PR is not exempt

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Retracting one clause of 5522300143.

What I wrote:"Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling."

Why that was an overreach: the framework chunk is attributed by path/[\\/]packages[\\/](core|react|types)[\\/]/. That argument worked for PR #7467 because its entire file list is plugin-timeline and i18n, so it structurally cannot add a byte to the chunk. This PR edits packages/types/src/objectql.ts (+47) and packages/types/src/zod/objectql.zod.ts (+82) — squarely inside the group. I reused #7467's conclusion on a PR whose premise does not hold, and I did it one message after writing that "any not mine claim still needs the path-list proof."

The measurement, now read from the job log

framework 511.9 KB / 511.7 KB ceiling (OVER by 0.2 KB)
EAGER_CLOSURE_BASE_SHA: 0246d116e ← a NEWER main than #7467's c1fe272ad

Against #7467's reading on c1fe272ad512.8 KB, over by 1.1 KB — two things moved at once:

  1. main improved. Something landed between c1fe272ad and 0246d116e that took roughly 0.9 KB back out of the chunk. main is no longer 1.1 KB over.
  2. This PR adds packages/types bytes on top of whatever main now carries, and I cannot separate the two from this run alone.

Unresolved, not exonerated. The honest statement is: main at 0246d116e may or may not still be over on its own, and this PR's +129 lines of packages/types may or may not be what carries it past 511.7. Deciding that needs a main-alone measurement at 0246d116e, which did not exist when I wrote the earlier comment and does not exist yet.

I have redirected the running measurement to take it. Until it reports:

  • ⛔ This PR is not cleared of its byte cost, and my earlier sentence should not be read as clearing it.
  • ⛔ Still no ceiling raise, and still no re-run — the build is deterministic.
  • The PR is Clause ② yes and draft regardless, so nothing was going to land on the strength of that sentence. The correction matters for the record, not for an action I took.

⭐ The general rule this violates is the one I had just written into my own check-in: while main is over, the per-chunk verdict carries no per-PR information, so every "not mine" claim needs its own path-list proof. A PR that edits packages/types never had one available.

Everything else in 5522300143 — the by-content verification, the ratified data declaration, the fence readings — stands unchanged.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS (content); landing blocked by the per-chunk eager-closure ceiling, not by this diff's correctness

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head afcb85788. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6939 ruling row for this group (recorded 2026-09-02, batch #8): objectName optional on both mirrors and both TypeScript twins; each member ends in requireRecordSource, root-path issue with params.code = 'RECORD_SOURCE_REQUIRED', presence by !== undefined (the ruling's wording, not the renderers' truthiness), so the accept set only widens. The one addition beyond the ruling's literal text — object-gantt declaring data — is the right call and is reported: it is the FIRST key the resolver reads and was undeclared on both faces, so a refinement naming it would otherwise have referenced a key the validator had never heard of; it is spelled exactly as the map's. The pin carries both halves: six catalog entries validate; render identity measured on d88e20f55 through the pins' own harness (the vacuous-harness trap is named and avoided), anti-vacuity on both; the gantt data counter-probe shows it is declared, not a passthrough hole. .shape survives the refinement (zod 4 in-place), so the parity census is unaffected, measured.

Semver / changeset.@object-ui/typespatch: widening only — objectName alone, empty included, still parses; the one refused shape was refused before. Correct.

Boundary flags. No governed path; plugin-map/tsconfig.test.json gains node types for the fixture read (the shape two sibling packages already use).

Landing — blocked, and not by this PR. Every check is green except Bundle Analysis: BUDGET_CLOSURE_STATUS: pass (3179.2 KB against 3191.4 KB), BUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399 ("the framework per-chunk ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land"). This PR is now the third such fix. The director seat presents #7399 to the maintainer in the next decision batch; until it is ruled this PR stays draft with its carrier cleared (the review is complete), and the objectui seat re-runs Bundle Analysis after the ruling lands rather than shaving bytes to fit under a ceiling whose size is the open question.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3180.0 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-DMoCUVyD.js
StatusPASS

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


📦 Bundle Size Report

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

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:08
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Landing — the ceiling this was parked behind is fixed. Contract PASS stands; ready + auto-merge.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance for the release action: the in-seat contract-review route (维护者 2026-08-31 裁定 —— 交付后复核由派发席在席内完成,PASS ⇒ 同席剥标、ready、auto-merge).

The blocker named in the review is gone

The contract review (5522971250) returned PASS on content and blocked landing on one thing only:

Every check is green except Bundle AnalysisBUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399.

#7399 was ruled (maintainer, 2026-09-03, option A′) and landed as 177afeba1 (PR #7489). The framework chunk was 78.7 % @object-ui/i18n locale catalogueframework and infrastructure tied at priority 80 and framework was written first — so the ceiling that rejected this PR was never budgeting core|react|types at all. The catalogue now has its own i18n-locales chunk and both ceilings were re-baselined in the same change.

main was merged in, and Bundle Analysis on 29e117bd is now ✅ PASS (3180.0 KB / 3191.4 KB, 50 chunks) — 5524564830. ⛔ Not one byte of this PR's own diff was changed to achieve it, no ceiling was raised, no test touched.

The three pre-landing conditions, checked rather than assumed

#conditionreading
in-seat contract PASS on record5522971250 — director seat, at CONTRACT_REVIEW_TIER, fuse read claude-fable-5-1 against claude-fable-5
needs:contract-review cleared on both carriers✅ by hand — PR labels package: types / plugin / examples / tests; card #6939 labels pm:dispatched / priority:p2 / domain:ui. ⚠️ see the caveat below
all checks green by name (⛔ not the required subset)✅ 32/32 — 27 success, 5 skipped (dependabot, the two coverage jobs, …). Bundle Analysis, Type Check, Lint, all four Test (shard N/4), Governed Surface Queue Guard each read individually

⭐ Why re-review was not required, established mechanically rather than assumed

The review was performed at head afcb85788; the head is now 29e117bd. A moved head normally means the gate label goes back on. It does not here, and this was checked, not asserted: the second commit is a pure Merge branch 'main', and

git diff --stat afcb85788 29e117bd -- <the 9 files this branch changes> → empty

⇒ the reviewed contract increment is byte-identical at the landing head. The merge resolved nothing in this branch's own files.

⚠️ Recorded against the clean reading of ②

node scripts/pm/check-clause2-carriers.mjs --pair 7471 is the machine check for the dual-carrier condition. It could not run in this session — it exits 2 with "the pair could not be formed, so nothing about it was judged", because this seat reaches GitHub only through MCP and the script has no token of its own. Its own output says that is ⛔ not a clearance, so I am not claiming a machine reading: ② above is a manual verification of both label sets. Flagging it because a hand check is the weaker instrument and the next seat should know which one was used.

⚠️ And one correction against myself

I had this PR recorded as "at the director seat, Clause ② yes — do not chase", and left it sitting on that basis. That was wrong. The rule is 「交付后复核由派发席在席内完成……PASS ⇒ 同席剥标、ready、auto-merge」 — the director seat's role here is optional post-hoc audit, not the release action. The review had been complete and passing since 08:36; the PR then waited on my misreading rather than on anything real. It also means I nearly commissioned a duplicate contract review of an already-passed increment, which I stopped once I read this thread.

Part of #6939 is deliberately not a closing keyword — the card stays open until all eight groups land. Group 6 of 8.


Generated by Claude Code

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.

2 participants

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

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8) - #7471

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement
Sep 3, 2026
Merged

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8)#7471
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #6939 — group 6 of 8 (object-map + object-gantt), dispatched as its own PR per the maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8, comment 5510084784). ⛔ Deliberately not a closing keyword: the card stays open until all eight groups land.

The defect

Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName. Both renderers read their records from one of THREE keys, in this order — measured at the read sites, not taken from the card:

  • packages/plugin-map/src/ObjectMap.tsxgetDataConfig (lines 122-167): if (schema.data) … if (schema.staticData) … if (schema.objectName) … return null
  • packages/plugin-gantt/src/ObjectGantt.tsxgetDataConfig (lines 312-331): the same three keys, the same order

No fourth source on either; all three are read by both. A document authored on staticData alone draws correctly and was refused by safeValidateSchema — six catalog entries, three per component, all staticData-only.

The shape

  • objectName is optional on both mirrors and on both TypeScript twins (packages/types/src/objectql.ts), moved in the same stroke.
  • Each member ends in requireRecordSource — a superRefine following the in-repo shape of FormFieldSchema's refinement in form.zod.ts: issue at the ROOT path (no single key is at fault, and blaming objectName would re-teach the requiredness this removes), params.code = 'RECORD_SOURCE_REQUIRED', message naming the three keys an author can supply. Presence is !== undefined — the ruling's wording — not the renderers' truthiness, so the accept set only widens (the changeset carries the argument).
  • object-gantt additionally declares data (ViewDataSchema, the map's spelling) on both faces. It is the FIRST key that resolver reads and was declared on neither face — a refinement over it would have named a key the validator had never heard of. Reported here as the one addition beyond the ruling's literal wording; it is a declared-equals-enforced restoration of the kind the card is about, and the parity ledger is unaffected because both faces gain the same pairing the map pair already carries.
  • Docs: the Schema API blocks of content/docs/plugins/plugin-map.mdx (which still published objectName: string) and plugin-gantt.mdx now say which of the three is required.
  • Changeset: @object-ui/types patch — the accept set only widens toward what already renders.

The pin — both halves the ruling asks for

Validator half (packages/types/src/__tests__/objectql-record-source-refinement-6939.test.ts): the six catalog entries validate (control: each authors no objectName and does author staticData); any one of the three alone validates; objectName: '' still parses (widening); a document with none of the three is refused ON THE REFINEMENT — exactly one issue, root path, RECORD_SOURCE_REQUIRED, all three key names in the message; .shape is still exposed on both objects (zod 4 attaches the check in place); gantt data is declared, not a passthrough hole.

Render half — BEFORE literals measured on origin/maind88e20f55 with the mirrors untouched, through the pin's OWN harness (a SchemaRendererProvider-wrapped bare SchemaRenderer, Date frozen). The harness is named because it matters twice over: a bare SchemaRenderer without the provider renders the error boundary for both components (4 elements, "failed to render") — an identity pin over that is the vacuous pin the ruling warns about — and the docs-gallery harness gives different absolute counts for the same tile.

  • gantt (examples/schema-catalog/test/objectql-record-source-render-identity-6939.test.tsx): 407 / 354 / 436 elements, a tag census, the visible text as a literal, and a SHA-256 of the full textContent (the chart's own style block is part of it). Identical before and after. Anti-vacuity: every authored task title on screen, no error box.
  • map (packages/plugin-map/src/ObjectMap.catalogRecordSource-6939.test.tsx): 16 / 14 / 14 elements, text = the geolocate glyph plus one marker glyph per row, tags in document order, 4 / 3 / 3 markers. Identical before and after. Anti-vacuity: one marker per staticData row, drawn on a LIVE maplibre stand-in (aria-label="Map" control). It lives in plugin-map because the same vi.mock('react-map-gl/maplibre') is measured INERT from examples/schema-catalog (the specifier does not resolve there; the real maplibre mounts and crashes on destroy).

Red-then-green ablation on the committed tree: restore both mirror sources to d88e20f55 (blob hashes checked equal to the base and unequal to HEAD; anchored LINE counts requireRecordSource 5 to 0, TS objectName?: string; 5 to 3, control ObjectMapSchema = BaseSchema.extend 1) → the three pins read 19 failed | 16 passed, exit 1 — every failure validator-facing; every render-identity, anti-vacuity and widening case green. Restore pinned to HEAD and proven by state: git diff HEAD empty, git status --porcelain empty, both blobs equal to HEAD, anchors back to 5 / 5.

Parity ledger

packages/types/src/__tests__/zod-mirror-parity.test.ts is UNTOUCHED. Measured before starting: neither objectql.zod.ts#ObjectMapSchema nor objectql.zod.ts#ObjectGanttSchema has an entry in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared; their only mention is the SPEC_DERIVED_PAIRS list, which is unaffected. Both faces moved together, so the pairs still record zero drift — asserted, not assumed: @object-ui/typestype-check (which compiles tsconfig.test.json) exit 0, and tsc -p tsconfig.test.json --listFiles lists the parity test, the new test and both edited sources (552 files in the program).

Verification (all at afcb85788, heavy runs serialized through the shared verify lock)

  • pnpm exec vitest run packages/types/ packages/plugin-map/ → Test Files 112 passed (112) / Tests 1662 passed (1662)
  • pnpm exec vitest run packages/plugin-gantt/ examples/schema-catalog/ → Test Files 81 passed (81) / Tests 2431 passed (2431)
  • the three pins alone → Test Files 3 passed / Tests 35 passed
  • type-check: @object-ui/types, @object-ui/plugin-map, @object-ui/plugin-gantt, @object-ui/example-schema-catalog — all exit 0 against a freshly built dependency closure (turbo build, 34 tasks)
  • lint for types / plugin-map / example-schema-catalog: 0 errors (267 + 141 pre-existing warnings, none in the new files)
  • gates, each exit 0 on its own verdict line: check:control-bytes, check:doc-fences, check:doc-types, check:doc-snippets (411 of 411 blocks compile), check:spec-symbols, check:vi-mock-specifiers, check:vi-mock-inherit, check:published-tsconfig-exclude, check:phantom-deps, type-check:coverage, changeset-presence, changeset-no-major
  • NOT MEASURED: check:readme-exports exits 1 only because plugin-ai's dist was not part of the built closure (3 unjudgeable self-imports; 403 judged, 0 fabricated); this diff edits no README and adds or removes no export.

Landing

Clause ② yes: this changes what a published mirror accepts. Labelled needs:contract-review, stays DRAFT, waits on the project director seat — not self-reviewed, not enqueued, no auto-merge. That is the expected end state.

Out of scope, filed separately: objectui#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true.

Session session_01EMrWaQw3XS5DxTHxp4yRyC.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…record-source refinement (objectui#6939, group 6 of 8)
Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName, a key
both renderers read THIRD: getDataConfig in plugin-map/src/ObjectMap.tsx and
plugin-gantt/src/ObjectGantt.tsx resolve records from data, then staticData, then
objectName. A document authored on staticData alone drew correctly and was refused
by safeValidateSchema — six catalog entries, three per component.
- objectName becomes optional on both mirrors and both TS twins in the same stroke.
- Each member ends in requireRecordSource: a superRefine whose issue sits at the root
path, carries params.code RECORD_SOURCE_REQUIRED and names data / staticData /
objectName. Presence is `!== undefined` (the ruling's wording), so the accept set
only widens: objectName alone, an empty one included, still parses.
- object-gantt additionally declares `data` (ViewDataSchema, the map's spelling): the
resolver's FIRST read was undeclared on both faces, which would have left the
refinement naming a key the validator had never heard of.
- Pins: validator contract in packages/types (six catalog entries validate; negative
arm checked by path / params.code / message), and render identity in each
renderer's own harness with BEFORE literals measured on d88e20f.
- Docs: the two plugin pages' Schema API blocks say which of the three is required.
Maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3179.2 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRlsZ8mv.js
StatusFAIL

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 halfVerdict
Aggregate closure ceiling✅ pass
Per-chunk ceilings❌ over its ceiling
Ceiling sensitivity (headroom)✅ pass
Ceiling freshness (checkout vs. base branch)✅ pass

📦 Bundle Size Report

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

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Seat bookkeeping — ⛔ NOT a contract review

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Clause ② yes, so this lane implements mechanically and ⛔ does not self-review, enqueue or auto-merge. needs:contract-review is on, the PR is draft, and it waits on the project director seat.

Verified on the branch by content, ⛔ not from the report

probeorigin/mainbranch afcb85788
objectName on the two ruled membersrequired at :584, :670optional at :640, :737, each with a describe naming the three sources
the other six objectName sites in the file66, unchanged
requireRecordSource / RECORD_SOURCE_REQUIREDabsentpresent, applied via .superRefine() on both members
⛔ FENCE — parity ledger, data-display.zod.ts, overlay.zod.ts, complex.zod.tsall four untouched
CONTROL — the diff reaches its targetobjectql.zod.ts +82, objectql.ts +47

The fence row is the one I care most about: five other groups of #6939 and three other in-flight PRs live in those files, and none of them moved.

⭐ The addition beyond the ruling's literal wording — correctly surfaced, and necessary

The ruling says only "objectName becomes optional with a refinement that at least one of data, staticData, objectName is present." The dev found that the gantt faces declared no data at all — the resolver's first read survived purely on BaseSchema's passthrough — and declared it, in the map pair's exact spelling. Measured: gantt data: declarations 2 → 3.

That is not scope creep, it is what makes the ruled refinement coherent: a refusal message naming data as an acceptable source, on a validator that had never heard of data, would tell authors to write a key the schema does not declare. ⇒ Ratified. The dev reported it rather than encoding it silently, which is the part that matters.

Two measurement details worth keeping

The vacuous pin was measured and discarded, not avoided by luck. A bare SchemaRenderer without the provider draws the error boundary for both components — 4 elements, "failed to render". Both tiles look identical before and after under that harness, so the render-identity claim would have passed for the wrong reason. The dev measured it first, threw it away, and states which harness the numbers are from.

A vi.mock was measured INERT.vi.mock('react-map-gl/maplibre') does not resolve from examples/schema-catalog, so real maplibre mounts and crashes; the map half therefore lives in packages/plugin-map. This repo has a check:vi-mock-specifiers gate for exactly that class, and the dev found the instance the gate would not have caught here.

The ablation split is the right shape: 19 red / 16 green, with every failure validator-facing and every render-identity, anti-vacuity and widening case green. Restore proven by state.

Filed, not acted on

#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true (plugin-map/src/index.tsx:67,77, plugin-gantt/src/index.tsx:107,117, restated in plugin-gantt/README.md:133) while the renderers read three record sources. Registrations were outside the ruling and are left as they stand. ⇒ After this PR the mirror and the designer input disagree; that is the follow-up's to reconcile, not this PR's to widen.

⚠️Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling. Measurement and path proof: #7399 comments 5522186641 / 5522268764, PR #7467 comment 5522177212.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ Correction to my own comment above — I called the Bundle Analysis red "not this PR's" without the proof, and this PR is not exempt

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Retracting one clause of 5522300143.

What I wrote:"Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling."

Why that was an overreach: the framework chunk is attributed by path/[\\/]packages[\\/](core|react|types)[\\/]/. That argument worked for PR #7467 because its entire file list is plugin-timeline and i18n, so it structurally cannot add a byte to the chunk. This PR edits packages/types/src/objectql.ts (+47) and packages/types/src/zod/objectql.zod.ts (+82) — squarely inside the group. I reused #7467's conclusion on a PR whose premise does not hold, and I did it one message after writing that "any not mine claim still needs the path-list proof."

The measurement, now read from the job log

framework 511.9 KB / 511.7 KB ceiling (OVER by 0.2 KB)
EAGER_CLOSURE_BASE_SHA: 0246d116e ← a NEWER main than #7467's c1fe272ad

Against #7467's reading on c1fe272ad512.8 KB, over by 1.1 KB — two things moved at once:

  1. main improved. Something landed between c1fe272ad and 0246d116e that took roughly 0.9 KB back out of the chunk. main is no longer 1.1 KB over.
  2. This PR adds packages/types bytes on top of whatever main now carries, and I cannot separate the two from this run alone.

Unresolved, not exonerated. The honest statement is: main at 0246d116e may or may not still be over on its own, and this PR's +129 lines of packages/types may or may not be what carries it past 511.7. Deciding that needs a main-alone measurement at 0246d116e, which did not exist when I wrote the earlier comment and does not exist yet.

I have redirected the running measurement to take it. Until it reports:

  • ⛔ This PR is not cleared of its byte cost, and my earlier sentence should not be read as clearing it.
  • ⛔ Still no ceiling raise, and still no re-run — the build is deterministic.
  • The PR is Clause ② yes and draft regardless, so nothing was going to land on the strength of that sentence. The correction matters for the record, not for an action I took.

⭐ The general rule this violates is the one I had just written into my own check-in: while main is over, the per-chunk verdict carries no per-PR information, so every "not mine" claim needs its own path-list proof. A PR that edits packages/types never had one available.

Everything else in 5522300143 — the by-content verification, the ratified data declaration, the fence readings — stands unchanged.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS (content); landing blocked by the per-chunk eager-closure ceiling, not by this diff's correctness

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head afcb85788. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6939 ruling row for this group (recorded 2026-09-02, batch #8): objectName optional on both mirrors and both TypeScript twins; each member ends in requireRecordSource, root-path issue with params.code = 'RECORD_SOURCE_REQUIRED', presence by !== undefined (the ruling's wording, not the renderers' truthiness), so the accept set only widens. The one addition beyond the ruling's literal text — object-gantt declaring data — is the right call and is reported: it is the FIRST key the resolver reads and was undeclared on both faces, so a refinement naming it would otherwise have referenced a key the validator had never heard of; it is spelled exactly as the map's. The pin carries both halves: six catalog entries validate; render identity measured on d88e20f55 through the pins' own harness (the vacuous-harness trap is named and avoided), anti-vacuity on both; the gantt data counter-probe shows it is declared, not a passthrough hole. .shape survives the refinement (zod 4 in-place), so the parity census is unaffected, measured.

Semver / changeset.@object-ui/typespatch: widening only — objectName alone, empty included, still parses; the one refused shape was refused before. Correct.

Boundary flags. No governed path; plugin-map/tsconfig.test.json gains node types for the fixture read (the shape two sibling packages already use).

Landing — blocked, and not by this PR. Every check is green except Bundle Analysis: BUDGET_CLOSURE_STATUS: pass (3179.2 KB against 3191.4 KB), BUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399 ("the framework per-chunk ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land"). This PR is now the third such fix. The director seat presents #7399 to the maintainer in the next decision batch; until it is ruled this PR stays draft with its carrier cleared (the review is complete), and the objectui seat re-runs Bundle Analysis after the ruling lands rather than shaving bytes to fit under a ceiling whose size is the open question.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3180.0 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-DMoCUVyD.js
StatusPASS

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


📦 Bundle Size Report

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

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:08
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Landing — the ceiling this was parked behind is fixed. Contract PASS stands; ready + auto-merge.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance for the release action: the in-seat contract-review route (维护者 2026-08-31 裁定 —— 交付后复核由派发席在席内完成,PASS ⇒ 同席剥标、ready、auto-merge).

The blocker named in the review is gone

The contract review (5522971250) returned PASS on content and blocked landing on one thing only:

Every check is green except Bundle AnalysisBUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399.

#7399 was ruled (maintainer, 2026-09-03, option A′) and landed as 177afeba1 (PR #7489). The framework chunk was 78.7 % @object-ui/i18n locale catalogueframework and infrastructure tied at priority 80 and framework was written first — so the ceiling that rejected this PR was never budgeting core|react|types at all. The catalogue now has its own i18n-locales chunk and both ceilings were re-baselined in the same change.

main was merged in, and Bundle Analysis on 29e117bd is now ✅ PASS (3180.0 KB / 3191.4 KB, 50 chunks) — 5524564830. ⛔ Not one byte of this PR's own diff was changed to achieve it, no ceiling was raised, no test touched.

The three pre-landing conditions, checked rather than assumed

#conditionreading
in-seat contract PASS on record5522971250 — director seat, at CONTRACT_REVIEW_TIER, fuse read claude-fable-5-1 against claude-fable-5
needs:contract-review cleared on both carriers✅ by hand — PR labels package: types / plugin / examples / tests; card #6939 labels pm:dispatched / priority:p2 / domain:ui. ⚠️ see the caveat below
all checks green by name (⛔ not the required subset)✅ 32/32 — 27 success, 5 skipped (dependabot, the two coverage jobs, …). Bundle Analysis, Type Check, Lint, all four Test (shard N/4), Governed Surface Queue Guard each read individually

⭐ Why re-review was not required, established mechanically rather than assumed

The review was performed at head afcb85788; the head is now 29e117bd. A moved head normally means the gate label goes back on. It does not here, and this was checked, not asserted: the second commit is a pure Merge branch 'main', and

git diff --stat afcb85788 29e117bd -- <the 9 files this branch changes> → empty

⇒ the reviewed contract increment is byte-identical at the landing head. The merge resolved nothing in this branch's own files.

⚠️ Recorded against the clean reading of ②

node scripts/pm/check-clause2-carriers.mjs --pair 7471 is the machine check for the dual-carrier condition. It could not run in this session — it exits 2 with "the pair could not be formed, so nothing about it was judged", because this seat reaches GitHub only through MCP and the script has no token of its own. Its own output says that is ⛔ not a clearance, so I am not claiming a machine reading: ② above is a manual verification of both label sets. Flagging it because a hand check is the weaker instrument and the next seat should know which one was used.

⚠️ And one correction against myself

I had this PR recorded as "at the director seat, Clause ② yes — do not chase", and left it sitting on that basis. That was wrong. The rule is 「交付后复核由派发席在席内完成……PASS ⇒ 同席剥标、ready、auto-merge」 — the director seat's role here is optional post-hoc audit, not the release action. The review had been complete and passing since 08:36; the PR then waited on my misreading rather than on anything real. It also means I nearly commissioned a duplicate contract review of an already-passed increment, which I stopped once I read this thread.

Part of #6939 is deliberately not a closing keyword — the card stays open until all eight groups land. Group 6 of 8.


Generated by Claude Code

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.

2 participants

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

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8) - #7471

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement
Sep 3, 2026
Merged

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8)#7471
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #6939 — group 6 of 8 (object-map + object-gantt), dispatched as its own PR per the maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8, comment 5510084784). ⛔ Deliberately not a closing keyword: the card stays open until all eight groups land.

The defect

Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName. Both renderers read their records from one of THREE keys, in this order — measured at the read sites, not taken from the card:

  • packages/plugin-map/src/ObjectMap.tsxgetDataConfig (lines 122-167): if (schema.data) … if (schema.staticData) … if (schema.objectName) … return null
  • packages/plugin-gantt/src/ObjectGantt.tsxgetDataConfig (lines 312-331): the same three keys, the same order

No fourth source on either; all three are read by both. A document authored on staticData alone draws correctly and was refused by safeValidateSchema — six catalog entries, three per component, all staticData-only.

The shape

  • objectName is optional on both mirrors and on both TypeScript twins (packages/types/src/objectql.ts), moved in the same stroke.
  • Each member ends in requireRecordSource — a superRefine following the in-repo shape of FormFieldSchema's refinement in form.zod.ts: issue at the ROOT path (no single key is at fault, and blaming objectName would re-teach the requiredness this removes), params.code = 'RECORD_SOURCE_REQUIRED', message naming the three keys an author can supply. Presence is !== undefined — the ruling's wording — not the renderers' truthiness, so the accept set only widens (the changeset carries the argument).
  • object-gantt additionally declares data (ViewDataSchema, the map's spelling) on both faces. It is the FIRST key that resolver reads and was declared on neither face — a refinement over it would have named a key the validator had never heard of. Reported here as the one addition beyond the ruling's literal wording; it is a declared-equals-enforced restoration of the kind the card is about, and the parity ledger is unaffected because both faces gain the same pairing the map pair already carries.
  • Docs: the Schema API blocks of content/docs/plugins/plugin-map.mdx (which still published objectName: string) and plugin-gantt.mdx now say which of the three is required.
  • Changeset: @object-ui/types patch — the accept set only widens toward what already renders.

The pin — both halves the ruling asks for

Validator half (packages/types/src/__tests__/objectql-record-source-refinement-6939.test.ts): the six catalog entries validate (control: each authors no objectName and does author staticData); any one of the three alone validates; objectName: '' still parses (widening); a document with none of the three is refused ON THE REFINEMENT — exactly one issue, root path, RECORD_SOURCE_REQUIRED, all three key names in the message; .shape is still exposed on both objects (zod 4 attaches the check in place); gantt data is declared, not a passthrough hole.

Render half — BEFORE literals measured on origin/maind88e20f55 with the mirrors untouched, through the pin's OWN harness (a SchemaRendererProvider-wrapped bare SchemaRenderer, Date frozen). The harness is named because it matters twice over: a bare SchemaRenderer without the provider renders the error boundary for both components (4 elements, "failed to render") — an identity pin over that is the vacuous pin the ruling warns about — and the docs-gallery harness gives different absolute counts for the same tile.

  • gantt (examples/schema-catalog/test/objectql-record-source-render-identity-6939.test.tsx): 407 / 354 / 436 elements, a tag census, the visible text as a literal, and a SHA-256 of the full textContent (the chart's own style block is part of it). Identical before and after. Anti-vacuity: every authored task title on screen, no error box.
  • map (packages/plugin-map/src/ObjectMap.catalogRecordSource-6939.test.tsx): 16 / 14 / 14 elements, text = the geolocate glyph plus one marker glyph per row, tags in document order, 4 / 3 / 3 markers. Identical before and after. Anti-vacuity: one marker per staticData row, drawn on a LIVE maplibre stand-in (aria-label="Map" control). It lives in plugin-map because the same vi.mock('react-map-gl/maplibre') is measured INERT from examples/schema-catalog (the specifier does not resolve there; the real maplibre mounts and crashes on destroy).

Red-then-green ablation on the committed tree: restore both mirror sources to d88e20f55 (blob hashes checked equal to the base and unequal to HEAD; anchored LINE counts requireRecordSource 5 to 0, TS objectName?: string; 5 to 3, control ObjectMapSchema = BaseSchema.extend 1) → the three pins read 19 failed | 16 passed, exit 1 — every failure validator-facing; every render-identity, anti-vacuity and widening case green. Restore pinned to HEAD and proven by state: git diff HEAD empty, git status --porcelain empty, both blobs equal to HEAD, anchors back to 5 / 5.

Parity ledger

packages/types/src/__tests__/zod-mirror-parity.test.ts is UNTOUCHED. Measured before starting: neither objectql.zod.ts#ObjectMapSchema nor objectql.zod.ts#ObjectGanttSchema has an entry in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared; their only mention is the SPEC_DERIVED_PAIRS list, which is unaffected. Both faces moved together, so the pairs still record zero drift — asserted, not assumed: @object-ui/typestype-check (which compiles tsconfig.test.json) exit 0, and tsc -p tsconfig.test.json --listFiles lists the parity test, the new test and both edited sources (552 files in the program).

Verification (all at afcb85788, heavy runs serialized through the shared verify lock)

  • pnpm exec vitest run packages/types/ packages/plugin-map/ → Test Files 112 passed (112) / Tests 1662 passed (1662)
  • pnpm exec vitest run packages/plugin-gantt/ examples/schema-catalog/ → Test Files 81 passed (81) / Tests 2431 passed (2431)
  • the three pins alone → Test Files 3 passed / Tests 35 passed
  • type-check: @object-ui/types, @object-ui/plugin-map, @object-ui/plugin-gantt, @object-ui/example-schema-catalog — all exit 0 against a freshly built dependency closure (turbo build, 34 tasks)
  • lint for types / plugin-map / example-schema-catalog: 0 errors (267 + 141 pre-existing warnings, none in the new files)
  • gates, each exit 0 on its own verdict line: check:control-bytes, check:doc-fences, check:doc-types, check:doc-snippets (411 of 411 blocks compile), check:spec-symbols, check:vi-mock-specifiers, check:vi-mock-inherit, check:published-tsconfig-exclude, check:phantom-deps, type-check:coverage, changeset-presence, changeset-no-major
  • NOT MEASURED: check:readme-exports exits 1 only because plugin-ai's dist was not part of the built closure (3 unjudgeable self-imports; 403 judged, 0 fabricated); this diff edits no README and adds or removes no export.

Landing

Clause ② yes: this changes what a published mirror accepts. Labelled needs:contract-review, stays DRAFT, waits on the project director seat — not self-reviewed, not enqueued, no auto-merge. That is the expected end state.

Out of scope, filed separately: objectui#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true.

Session session_01EMrWaQw3XS5DxTHxp4yRyC.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…record-source refinement (objectui#6939, group 6 of 8)
Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName, a key
both renderers read THIRD: getDataConfig in plugin-map/src/ObjectMap.tsx and
plugin-gantt/src/ObjectGantt.tsx resolve records from data, then staticData, then
objectName. A document authored on staticData alone drew correctly and was refused
by safeValidateSchema — six catalog entries, three per component.
- objectName becomes optional on both mirrors and both TS twins in the same stroke.
- Each member ends in requireRecordSource: a superRefine whose issue sits at the root
path, carries params.code RECORD_SOURCE_REQUIRED and names data / staticData /
objectName. Presence is `!== undefined` (the ruling's wording), so the accept set
only widens: objectName alone, an empty one included, still parses.
- object-gantt additionally declares `data` (ViewDataSchema, the map's spelling): the
resolver's FIRST read was undeclared on both faces, which would have left the
refinement naming a key the validator had never heard of.
- Pins: validator contract in packages/types (six catalog entries validate; negative
arm checked by path / params.code / message), and render identity in each
renderer's own harness with BEFORE literals measured on d88e20f.
- Docs: the two plugin pages' Schema API blocks say which of the three is required.
Maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3179.2 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRlsZ8mv.js
StatusFAIL

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 halfVerdict
Aggregate closure ceiling✅ pass
Per-chunk ceilings❌ over its ceiling
Ceiling sensitivity (headroom)✅ pass
Ceiling freshness (checkout vs. base branch)✅ pass

📦 Bundle Size Report

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

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Seat bookkeeping — ⛔ NOT a contract review

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Clause ② yes, so this lane implements mechanically and ⛔ does not self-review, enqueue or auto-merge. needs:contract-review is on, the PR is draft, and it waits on the project director seat.

Verified on the branch by content, ⛔ not from the report

probeorigin/mainbranch afcb85788
objectName on the two ruled membersrequired at :584, :670optional at :640, :737, each with a describe naming the three sources
the other six objectName sites in the file66, unchanged
requireRecordSource / RECORD_SOURCE_REQUIREDabsentpresent, applied via .superRefine() on both members
⛔ FENCE — parity ledger, data-display.zod.ts, overlay.zod.ts, complex.zod.tsall four untouched
CONTROL — the diff reaches its targetobjectql.zod.ts +82, objectql.ts +47

The fence row is the one I care most about: five other groups of #6939 and three other in-flight PRs live in those files, and none of them moved.

⭐ The addition beyond the ruling's literal wording — correctly surfaced, and necessary

The ruling says only "objectName becomes optional with a refinement that at least one of data, staticData, objectName is present." The dev found that the gantt faces declared no data at all — the resolver's first read survived purely on BaseSchema's passthrough — and declared it, in the map pair's exact spelling. Measured: gantt data: declarations 2 → 3.

That is not scope creep, it is what makes the ruled refinement coherent: a refusal message naming data as an acceptable source, on a validator that had never heard of data, would tell authors to write a key the schema does not declare. ⇒ Ratified. The dev reported it rather than encoding it silently, which is the part that matters.

Two measurement details worth keeping

The vacuous pin was measured and discarded, not avoided by luck. A bare SchemaRenderer without the provider draws the error boundary for both components — 4 elements, "failed to render". Both tiles look identical before and after under that harness, so the render-identity claim would have passed for the wrong reason. The dev measured it first, threw it away, and states which harness the numbers are from.

A vi.mock was measured INERT.vi.mock('react-map-gl/maplibre') does not resolve from examples/schema-catalog, so real maplibre mounts and crashes; the map half therefore lives in packages/plugin-map. This repo has a check:vi-mock-specifiers gate for exactly that class, and the dev found the instance the gate would not have caught here.

The ablation split is the right shape: 19 red / 16 green, with every failure validator-facing and every render-identity, anti-vacuity and widening case green. Restore proven by state.

Filed, not acted on

#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true (plugin-map/src/index.tsx:67,77, plugin-gantt/src/index.tsx:107,117, restated in plugin-gantt/README.md:133) while the renderers read three record sources. Registrations were outside the ruling and are left as they stand. ⇒ After this PR the mirror and the designer input disagree; that is the follow-up's to reconcile, not this PR's to widen.

⚠️Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling. Measurement and path proof: #7399 comments 5522186641 / 5522268764, PR #7467 comment 5522177212.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ Correction to my own comment above — I called the Bundle Analysis red "not this PR's" without the proof, and this PR is not exempt

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Retracting one clause of 5522300143.

What I wrote:"Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling."

Why that was an overreach: the framework chunk is attributed by path/[\\/]packages[\\/](core|react|types)[\\/]/. That argument worked for PR #7467 because its entire file list is plugin-timeline and i18n, so it structurally cannot add a byte to the chunk. This PR edits packages/types/src/objectql.ts (+47) and packages/types/src/zod/objectql.zod.ts (+82) — squarely inside the group. I reused #7467's conclusion on a PR whose premise does not hold, and I did it one message after writing that "any not mine claim still needs the path-list proof."

The measurement, now read from the job log

framework 511.9 KB / 511.7 KB ceiling (OVER by 0.2 KB)
EAGER_CLOSURE_BASE_SHA: 0246d116e ← a NEWER main than #7467's c1fe272ad

Against #7467's reading on c1fe272ad512.8 KB, over by 1.1 KB — two things moved at once:

  1. main improved. Something landed between c1fe272ad and 0246d116e that took roughly 0.9 KB back out of the chunk. main is no longer 1.1 KB over.
  2. This PR adds packages/types bytes on top of whatever main now carries, and I cannot separate the two from this run alone.

Unresolved, not exonerated. The honest statement is: main at 0246d116e may or may not still be over on its own, and this PR's +129 lines of packages/types may or may not be what carries it past 511.7. Deciding that needs a main-alone measurement at 0246d116e, which did not exist when I wrote the earlier comment and does not exist yet.

I have redirected the running measurement to take it. Until it reports:

  • ⛔ This PR is not cleared of its byte cost, and my earlier sentence should not be read as clearing it.
  • ⛔ Still no ceiling raise, and still no re-run — the build is deterministic.
  • The PR is Clause ② yes and draft regardless, so nothing was going to land on the strength of that sentence. The correction matters for the record, not for an action I took.

⭐ The general rule this violates is the one I had just written into my own check-in: while main is over, the per-chunk verdict carries no per-PR information, so every "not mine" claim needs its own path-list proof. A PR that edits packages/types never had one available.

Everything else in 5522300143 — the by-content verification, the ratified data declaration, the fence readings — stands unchanged.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS (content); landing blocked by the per-chunk eager-closure ceiling, not by this diff's correctness

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head afcb85788. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6939 ruling row for this group (recorded 2026-09-02, batch #8): objectName optional on both mirrors and both TypeScript twins; each member ends in requireRecordSource, root-path issue with params.code = 'RECORD_SOURCE_REQUIRED', presence by !== undefined (the ruling's wording, not the renderers' truthiness), so the accept set only widens. The one addition beyond the ruling's literal text — object-gantt declaring data — is the right call and is reported: it is the FIRST key the resolver reads and was undeclared on both faces, so a refinement naming it would otherwise have referenced a key the validator had never heard of; it is spelled exactly as the map's. The pin carries both halves: six catalog entries validate; render identity measured on d88e20f55 through the pins' own harness (the vacuous-harness trap is named and avoided), anti-vacuity on both; the gantt data counter-probe shows it is declared, not a passthrough hole. .shape survives the refinement (zod 4 in-place), so the parity census is unaffected, measured.

Semver / changeset.@object-ui/typespatch: widening only — objectName alone, empty included, still parses; the one refused shape was refused before. Correct.

Boundary flags. No governed path; plugin-map/tsconfig.test.json gains node types for the fixture read (the shape two sibling packages already use).

Landing — blocked, and not by this PR. Every check is green except Bundle Analysis: BUDGET_CLOSURE_STATUS: pass (3179.2 KB against 3191.4 KB), BUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399 ("the framework per-chunk ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land"). This PR is now the third such fix. The director seat presents #7399 to the maintainer in the next decision batch; until it is ruled this PR stays draft with its carrier cleared (the review is complete), and the objectui seat re-runs Bundle Analysis after the ruling lands rather than shaving bytes to fit under a ceiling whose size is the open question.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3180.0 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-DMoCUVyD.js
StatusPASS

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


📦 Bundle Size Report

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

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:08
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Landing — the ceiling this was parked behind is fixed. Contract PASS stands; ready + auto-merge.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance for the release action: the in-seat contract-review route (维护者 2026-08-31 裁定 —— 交付后复核由派发席在席内完成,PASS ⇒ 同席剥标、ready、auto-merge).

The blocker named in the review is gone

The contract review (5522971250) returned PASS on content and blocked landing on one thing only:

Every check is green except Bundle AnalysisBUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399.

#7399 was ruled (maintainer, 2026-09-03, option A′) and landed as 177afeba1 (PR #7489). The framework chunk was 78.7 % @object-ui/i18n locale catalogueframework and infrastructure tied at priority 80 and framework was written first — so the ceiling that rejected this PR was never budgeting core|react|types at all. The catalogue now has its own i18n-locales chunk and both ceilings were re-baselined in the same change.

main was merged in, and Bundle Analysis on 29e117bd is now ✅ PASS (3180.0 KB / 3191.4 KB, 50 chunks) — 5524564830. ⛔ Not one byte of this PR's own diff was changed to achieve it, no ceiling was raised, no test touched.

The three pre-landing conditions, checked rather than assumed

#conditionreading
in-seat contract PASS on record5522971250 — director seat, at CONTRACT_REVIEW_TIER, fuse read claude-fable-5-1 against claude-fable-5
needs:contract-review cleared on both carriers✅ by hand — PR labels package: types / plugin / examples / tests; card #6939 labels pm:dispatched / priority:p2 / domain:ui. ⚠️ see the caveat below
all checks green by name (⛔ not the required subset)✅ 32/32 — 27 success, 5 skipped (dependabot, the two coverage jobs, …). Bundle Analysis, Type Check, Lint, all four Test (shard N/4), Governed Surface Queue Guard each read individually

⭐ Why re-review was not required, established mechanically rather than assumed

The review was performed at head afcb85788; the head is now 29e117bd. A moved head normally means the gate label goes back on. It does not here, and this was checked, not asserted: the second commit is a pure Merge branch 'main', and

git diff --stat afcb85788 29e117bd -- <the 9 files this branch changes> → empty

⇒ the reviewed contract increment is byte-identical at the landing head. The merge resolved nothing in this branch's own files.

⚠️ Recorded against the clean reading of ②

node scripts/pm/check-clause2-carriers.mjs --pair 7471 is the machine check for the dual-carrier condition. It could not run in this session — it exits 2 with "the pair could not be formed, so nothing about it was judged", because this seat reaches GitHub only through MCP and the script has no token of its own. Its own output says that is ⛔ not a clearance, so I am not claiming a machine reading: ② above is a manual verification of both label sets. Flagging it because a hand check is the weaker instrument and the next seat should know which one was used.

⚠️ And one correction against myself

I had this PR recorded as "at the director seat, Clause ② yes — do not chase", and left it sitting on that basis. That was wrong. The rule is 「交付后复核由派发席在席内完成……PASS ⇒ 同席剥标、ready、auto-merge」 — the director seat's role here is optional post-hoc audit, not the release action. The review had been complete and passing since 08:36; the PR then waited on my misreading rather than on anything real. It also means I nearly commissioned a duplicate contract review of an already-passed increment, which I stopped once I read this thread.

Part of #6939 is deliberately not a closing keyword — the card stays open until all eight groups land. Group 6 of 8.


Generated by Claude Code

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.

2 participants

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

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8) - #7471

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement
Sep 3, 2026
Merged

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8)#7471
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #6939 — group 6 of 8 (object-map + object-gantt), dispatched as its own PR per the maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8, comment 5510084784). ⛔ Deliberately not a closing keyword: the card stays open until all eight groups land.

The defect

Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName. Both renderers read their records from one of THREE keys, in this order — measured at the read sites, not taken from the card:

  • packages/plugin-map/src/ObjectMap.tsxgetDataConfig (lines 122-167): if (schema.data) … if (schema.staticData) … if (schema.objectName) … return null
  • packages/plugin-gantt/src/ObjectGantt.tsxgetDataConfig (lines 312-331): the same three keys, the same order

No fourth source on either; all three are read by both. A document authored on staticData alone draws correctly and was refused by safeValidateSchema — six catalog entries, three per component, all staticData-only.

The shape

  • objectName is optional on both mirrors and on both TypeScript twins (packages/types/src/objectql.ts), moved in the same stroke.
  • Each member ends in requireRecordSource — a superRefine following the in-repo shape of FormFieldSchema's refinement in form.zod.ts: issue at the ROOT path (no single key is at fault, and blaming objectName would re-teach the requiredness this removes), params.code = 'RECORD_SOURCE_REQUIRED', message naming the three keys an author can supply. Presence is !== undefined — the ruling's wording — not the renderers' truthiness, so the accept set only widens (the changeset carries the argument).
  • object-gantt additionally declares data (ViewDataSchema, the map's spelling) on both faces. It is the FIRST key that resolver reads and was declared on neither face — a refinement over it would have named a key the validator had never heard of. Reported here as the one addition beyond the ruling's literal wording; it is a declared-equals-enforced restoration of the kind the card is about, and the parity ledger is unaffected because both faces gain the same pairing the map pair already carries.
  • Docs: the Schema API blocks of content/docs/plugins/plugin-map.mdx (which still published objectName: string) and plugin-gantt.mdx now say which of the three is required.
  • Changeset: @object-ui/types patch — the accept set only widens toward what already renders.

The pin — both halves the ruling asks for

Validator half (packages/types/src/__tests__/objectql-record-source-refinement-6939.test.ts): the six catalog entries validate (control: each authors no objectName and does author staticData); any one of the three alone validates; objectName: '' still parses (widening); a document with none of the three is refused ON THE REFINEMENT — exactly one issue, root path, RECORD_SOURCE_REQUIRED, all three key names in the message; .shape is still exposed on both objects (zod 4 attaches the check in place); gantt data is declared, not a passthrough hole.

Render half — BEFORE literals measured on origin/maind88e20f55 with the mirrors untouched, through the pin's OWN harness (a SchemaRendererProvider-wrapped bare SchemaRenderer, Date frozen). The harness is named because it matters twice over: a bare SchemaRenderer without the provider renders the error boundary for both components (4 elements, "failed to render") — an identity pin over that is the vacuous pin the ruling warns about — and the docs-gallery harness gives different absolute counts for the same tile.

  • gantt (examples/schema-catalog/test/objectql-record-source-render-identity-6939.test.tsx): 407 / 354 / 436 elements, a tag census, the visible text as a literal, and a SHA-256 of the full textContent (the chart's own style block is part of it). Identical before and after. Anti-vacuity: every authored task title on screen, no error box.
  • map (packages/plugin-map/src/ObjectMap.catalogRecordSource-6939.test.tsx): 16 / 14 / 14 elements, text = the geolocate glyph plus one marker glyph per row, tags in document order, 4 / 3 / 3 markers. Identical before and after. Anti-vacuity: one marker per staticData row, drawn on a LIVE maplibre stand-in (aria-label="Map" control). It lives in plugin-map because the same vi.mock('react-map-gl/maplibre') is measured INERT from examples/schema-catalog (the specifier does not resolve there; the real maplibre mounts and crashes on destroy).

Red-then-green ablation on the committed tree: restore both mirror sources to d88e20f55 (blob hashes checked equal to the base and unequal to HEAD; anchored LINE counts requireRecordSource 5 to 0, TS objectName?: string; 5 to 3, control ObjectMapSchema = BaseSchema.extend 1) → the three pins read 19 failed | 16 passed, exit 1 — every failure validator-facing; every render-identity, anti-vacuity and widening case green. Restore pinned to HEAD and proven by state: git diff HEAD empty, git status --porcelain empty, both blobs equal to HEAD, anchors back to 5 / 5.

Parity ledger

packages/types/src/__tests__/zod-mirror-parity.test.ts is UNTOUCHED. Measured before starting: neither objectql.zod.ts#ObjectMapSchema nor objectql.zod.ts#ObjectGanttSchema has an entry in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared; their only mention is the SPEC_DERIVED_PAIRS list, which is unaffected. Both faces moved together, so the pairs still record zero drift — asserted, not assumed: @object-ui/typestype-check (which compiles tsconfig.test.json) exit 0, and tsc -p tsconfig.test.json --listFiles lists the parity test, the new test and both edited sources (552 files in the program).

Verification (all at afcb85788, heavy runs serialized through the shared verify lock)

  • pnpm exec vitest run packages/types/ packages/plugin-map/ → Test Files 112 passed (112) / Tests 1662 passed (1662)
  • pnpm exec vitest run packages/plugin-gantt/ examples/schema-catalog/ → Test Files 81 passed (81) / Tests 2431 passed (2431)
  • the three pins alone → Test Files 3 passed / Tests 35 passed
  • type-check: @object-ui/types, @object-ui/plugin-map, @object-ui/plugin-gantt, @object-ui/example-schema-catalog — all exit 0 against a freshly built dependency closure (turbo build, 34 tasks)
  • lint for types / plugin-map / example-schema-catalog: 0 errors (267 + 141 pre-existing warnings, none in the new files)
  • gates, each exit 0 on its own verdict line: check:control-bytes, check:doc-fences, check:doc-types, check:doc-snippets (411 of 411 blocks compile), check:spec-symbols, check:vi-mock-specifiers, check:vi-mock-inherit, check:published-tsconfig-exclude, check:phantom-deps, type-check:coverage, changeset-presence, changeset-no-major
  • NOT MEASURED: check:readme-exports exits 1 only because plugin-ai's dist was not part of the built closure (3 unjudgeable self-imports; 403 judged, 0 fabricated); this diff edits no README and adds or removes no export.

Landing

Clause ② yes: this changes what a published mirror accepts. Labelled needs:contract-review, stays DRAFT, waits on the project director seat — not self-reviewed, not enqueued, no auto-merge. That is the expected end state.

Out of scope, filed separately: objectui#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true.

Session session_01EMrWaQw3XS5DxTHxp4yRyC.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…record-source refinement (objectui#6939, group 6 of 8)
Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName, a key
both renderers read THIRD: getDataConfig in plugin-map/src/ObjectMap.tsx and
plugin-gantt/src/ObjectGantt.tsx resolve records from data, then staticData, then
objectName. A document authored on staticData alone drew correctly and was refused
by safeValidateSchema — six catalog entries, three per component.
- objectName becomes optional on both mirrors and both TS twins in the same stroke.
- Each member ends in requireRecordSource: a superRefine whose issue sits at the root
path, carries params.code RECORD_SOURCE_REQUIRED and names data / staticData /
objectName. Presence is `!== undefined` (the ruling's wording), so the accept set
only widens: objectName alone, an empty one included, still parses.
- object-gantt additionally declares `data` (ViewDataSchema, the map's spelling): the
resolver's FIRST read was undeclared on both faces, which would have left the
refinement naming a key the validator had never heard of.
- Pins: validator contract in packages/types (six catalog entries validate; negative
arm checked by path / params.code / message), and render identity in each
renderer's own harness with BEFORE literals measured on d88e20f.
- Docs: the two plugin pages' Schema API blocks say which of the three is required.
Maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3179.2 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRlsZ8mv.js
StatusFAIL

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 halfVerdict
Aggregate closure ceiling✅ pass
Per-chunk ceilings❌ over its ceiling
Ceiling sensitivity (headroom)✅ pass
Ceiling freshness (checkout vs. base branch)✅ pass

📦 Bundle Size Report

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

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Seat bookkeeping — ⛔ NOT a contract review

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Clause ② yes, so this lane implements mechanically and ⛔ does not self-review, enqueue or auto-merge. needs:contract-review is on, the PR is draft, and it waits on the project director seat.

Verified on the branch by content, ⛔ not from the report

probeorigin/mainbranch afcb85788
objectName on the two ruled membersrequired at :584, :670optional at :640, :737, each with a describe naming the three sources
the other six objectName sites in the file66, unchanged
requireRecordSource / RECORD_SOURCE_REQUIREDabsentpresent, applied via .superRefine() on both members
⛔ FENCE — parity ledger, data-display.zod.ts, overlay.zod.ts, complex.zod.tsall four untouched
CONTROL — the diff reaches its targetobjectql.zod.ts +82, objectql.ts +47

The fence row is the one I care most about: five other groups of #6939 and three other in-flight PRs live in those files, and none of them moved.

⭐ The addition beyond the ruling's literal wording — correctly surfaced, and necessary

The ruling says only "objectName becomes optional with a refinement that at least one of data, staticData, objectName is present." The dev found that the gantt faces declared no data at all — the resolver's first read survived purely on BaseSchema's passthrough — and declared it, in the map pair's exact spelling. Measured: gantt data: declarations 2 → 3.

That is not scope creep, it is what makes the ruled refinement coherent: a refusal message naming data as an acceptable source, on a validator that had never heard of data, would tell authors to write a key the schema does not declare. ⇒ Ratified. The dev reported it rather than encoding it silently, which is the part that matters.

Two measurement details worth keeping

The vacuous pin was measured and discarded, not avoided by luck. A bare SchemaRenderer without the provider draws the error boundary for both components — 4 elements, "failed to render". Both tiles look identical before and after under that harness, so the render-identity claim would have passed for the wrong reason. The dev measured it first, threw it away, and states which harness the numbers are from.

A vi.mock was measured INERT.vi.mock('react-map-gl/maplibre') does not resolve from examples/schema-catalog, so real maplibre mounts and crashes; the map half therefore lives in packages/plugin-map. This repo has a check:vi-mock-specifiers gate for exactly that class, and the dev found the instance the gate would not have caught here.

The ablation split is the right shape: 19 red / 16 green, with every failure validator-facing and every render-identity, anti-vacuity and widening case green. Restore proven by state.

Filed, not acted on

#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true (plugin-map/src/index.tsx:67,77, plugin-gantt/src/index.tsx:107,117, restated in plugin-gantt/README.md:133) while the renderers read three record sources. Registrations were outside the ruling and are left as they stand. ⇒ After this PR the mirror and the designer input disagree; that is the follow-up's to reconcile, not this PR's to widen.

⚠️Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling. Measurement and path proof: #7399 comments 5522186641 / 5522268764, PR #7467 comment 5522177212.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ Correction to my own comment above — I called the Bundle Analysis red "not this PR's" without the proof, and this PR is not exempt

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Retracting one clause of 5522300143.

What I wrote:"Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling."

Why that was an overreach: the framework chunk is attributed by path/[\\/]packages[\\/](core|react|types)[\\/]/. That argument worked for PR #7467 because its entire file list is plugin-timeline and i18n, so it structurally cannot add a byte to the chunk. This PR edits packages/types/src/objectql.ts (+47) and packages/types/src/zod/objectql.zod.ts (+82) — squarely inside the group. I reused #7467's conclusion on a PR whose premise does not hold, and I did it one message after writing that "any not mine claim still needs the path-list proof."

The measurement, now read from the job log

framework 511.9 KB / 511.7 KB ceiling (OVER by 0.2 KB)
EAGER_CLOSURE_BASE_SHA: 0246d116e ← a NEWER main than #7467's c1fe272ad

Against #7467's reading on c1fe272ad512.8 KB, over by 1.1 KB — two things moved at once:

  1. main improved. Something landed between c1fe272ad and 0246d116e that took roughly 0.9 KB back out of the chunk. main is no longer 1.1 KB over.
  2. This PR adds packages/types bytes on top of whatever main now carries, and I cannot separate the two from this run alone.

Unresolved, not exonerated. The honest statement is: main at 0246d116e may or may not still be over on its own, and this PR's +129 lines of packages/types may or may not be what carries it past 511.7. Deciding that needs a main-alone measurement at 0246d116e, which did not exist when I wrote the earlier comment and does not exist yet.

I have redirected the running measurement to take it. Until it reports:

  • ⛔ This PR is not cleared of its byte cost, and my earlier sentence should not be read as clearing it.
  • ⛔ Still no ceiling raise, and still no re-run — the build is deterministic.
  • The PR is Clause ② yes and draft regardless, so nothing was going to land on the strength of that sentence. The correction matters for the record, not for an action I took.

⭐ The general rule this violates is the one I had just written into my own check-in: while main is over, the per-chunk verdict carries no per-PR information, so every "not mine" claim needs its own path-list proof. A PR that edits packages/types never had one available.

Everything else in 5522300143 — the by-content verification, the ratified data declaration, the fence readings — stands unchanged.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS (content); landing blocked by the per-chunk eager-closure ceiling, not by this diff's correctness

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head afcb85788. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6939 ruling row for this group (recorded 2026-09-02, batch #8): objectName optional on both mirrors and both TypeScript twins; each member ends in requireRecordSource, root-path issue with params.code = 'RECORD_SOURCE_REQUIRED', presence by !== undefined (the ruling's wording, not the renderers' truthiness), so the accept set only widens. The one addition beyond the ruling's literal text — object-gantt declaring data — is the right call and is reported: it is the FIRST key the resolver reads and was undeclared on both faces, so a refinement naming it would otherwise have referenced a key the validator had never heard of; it is spelled exactly as the map's. The pin carries both halves: six catalog entries validate; render identity measured on d88e20f55 through the pins' own harness (the vacuous-harness trap is named and avoided), anti-vacuity on both; the gantt data counter-probe shows it is declared, not a passthrough hole. .shape survives the refinement (zod 4 in-place), so the parity census is unaffected, measured.

Semver / changeset.@object-ui/typespatch: widening only — objectName alone, empty included, still parses; the one refused shape was refused before. Correct.

Boundary flags. No governed path; plugin-map/tsconfig.test.json gains node types for the fixture read (the shape two sibling packages already use).

Landing — blocked, and not by this PR. Every check is green except Bundle Analysis: BUDGET_CLOSURE_STATUS: pass (3179.2 KB against 3191.4 KB), BUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399 ("the framework per-chunk ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land"). This PR is now the third such fix. The director seat presents #7399 to the maintainer in the next decision batch; until it is ruled this PR stays draft with its carrier cleared (the review is complete), and the objectui seat re-runs Bundle Analysis after the ruling lands rather than shaving bytes to fit under a ceiling whose size is the open question.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3180.0 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-DMoCUVyD.js
StatusPASS

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


📦 Bundle Size Report

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

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:08
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Landing — the ceiling this was parked behind is fixed. Contract PASS stands; ready + auto-merge.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance for the release action: the in-seat contract-review route (维护者 2026-08-31 裁定 —— 交付后复核由派发席在席内完成,PASS ⇒ 同席剥标、ready、auto-merge).

The blocker named in the review is gone

The contract review (5522971250) returned PASS on content and blocked landing on one thing only:

Every check is green except Bundle AnalysisBUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399.

#7399 was ruled (maintainer, 2026-09-03, option A′) and landed as 177afeba1 (PR #7489). The framework chunk was 78.7 % @object-ui/i18n locale catalogueframework and infrastructure tied at priority 80 and framework was written first — so the ceiling that rejected this PR was never budgeting core|react|types at all. The catalogue now has its own i18n-locales chunk and both ceilings were re-baselined in the same change.

main was merged in, and Bundle Analysis on 29e117bd is now ✅ PASS (3180.0 KB / 3191.4 KB, 50 chunks) — 5524564830. ⛔ Not one byte of this PR's own diff was changed to achieve it, no ceiling was raised, no test touched.

The three pre-landing conditions, checked rather than assumed

#conditionreading
in-seat contract PASS on record5522971250 — director seat, at CONTRACT_REVIEW_TIER, fuse read claude-fable-5-1 against claude-fable-5
needs:contract-review cleared on both carriers✅ by hand — PR labels package: types / plugin / examples / tests; card #6939 labels pm:dispatched / priority:p2 / domain:ui. ⚠️ see the caveat below
all checks green by name (⛔ not the required subset)✅ 32/32 — 27 success, 5 skipped (dependabot, the two coverage jobs, …). Bundle Analysis, Type Check, Lint, all four Test (shard N/4), Governed Surface Queue Guard each read individually

⭐ Why re-review was not required, established mechanically rather than assumed

The review was performed at head afcb85788; the head is now 29e117bd. A moved head normally means the gate label goes back on. It does not here, and this was checked, not asserted: the second commit is a pure Merge branch 'main', and

git diff --stat afcb85788 29e117bd -- <the 9 files this branch changes> → empty

⇒ the reviewed contract increment is byte-identical at the landing head. The merge resolved nothing in this branch's own files.

⚠️ Recorded against the clean reading of ②

node scripts/pm/check-clause2-carriers.mjs --pair 7471 is the machine check for the dual-carrier condition. It could not run in this session — it exits 2 with "the pair could not be formed, so nothing about it was judged", because this seat reaches GitHub only through MCP and the script has no token of its own. Its own output says that is ⛔ not a clearance, so I am not claiming a machine reading: ② above is a manual verification of both label sets. Flagging it because a hand check is the weaker instrument and the next seat should know which one was used.

⚠️ And one correction against myself

I had this PR recorded as "at the director seat, Clause ② yes — do not chase", and left it sitting on that basis. That was wrong. The rule is 「交付后复核由派发席在席内完成……PASS ⇒ 同席剥标、ready、auto-merge」 — the director seat's role here is optional post-hoc audit, not the release action. The review had been complete and passing since 08:36; the PR then waited on my misreading rather than on anything real. It also means I nearly commissioned a duplicate contract review of an already-passed increment, which I stopped once I read this thread.

Part of #6939 is deliberately not a closing keyword — the card stays open until all eight groups land. Group 6 of 8.


Generated by Claude Code

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.

2 participants

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

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8) - #7471

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement
Sep 3, 2026
Merged

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8)#7471
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #6939 — group 6 of 8 (object-map + object-gantt), dispatched as its own PR per the maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8, comment 5510084784). ⛔ Deliberately not a closing keyword: the card stays open until all eight groups land.

The defect

Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName. Both renderers read their records from one of THREE keys, in this order — measured at the read sites, not taken from the card:

  • packages/plugin-map/src/ObjectMap.tsxgetDataConfig (lines 122-167): if (schema.data) … if (schema.staticData) … if (schema.objectName) … return null
  • packages/plugin-gantt/src/ObjectGantt.tsxgetDataConfig (lines 312-331): the same three keys, the same order

No fourth source on either; all three are read by both. A document authored on staticData alone draws correctly and was refused by safeValidateSchema — six catalog entries, three per component, all staticData-only.

The shape

  • objectName is optional on both mirrors and on both TypeScript twins (packages/types/src/objectql.ts), moved in the same stroke.
  • Each member ends in requireRecordSource — a superRefine following the in-repo shape of FormFieldSchema's refinement in form.zod.ts: issue at the ROOT path (no single key is at fault, and blaming objectName would re-teach the requiredness this removes), params.code = 'RECORD_SOURCE_REQUIRED', message naming the three keys an author can supply. Presence is !== undefined — the ruling's wording — not the renderers' truthiness, so the accept set only widens (the changeset carries the argument).
  • object-gantt additionally declares data (ViewDataSchema, the map's spelling) on both faces. It is the FIRST key that resolver reads and was declared on neither face — a refinement over it would have named a key the validator had never heard of. Reported here as the one addition beyond the ruling's literal wording; it is a declared-equals-enforced restoration of the kind the card is about, and the parity ledger is unaffected because both faces gain the same pairing the map pair already carries.
  • Docs: the Schema API blocks of content/docs/plugins/plugin-map.mdx (which still published objectName: string) and plugin-gantt.mdx now say which of the three is required.
  • Changeset: @object-ui/types patch — the accept set only widens toward what already renders.

The pin — both halves the ruling asks for

Validator half (packages/types/src/__tests__/objectql-record-source-refinement-6939.test.ts): the six catalog entries validate (control: each authors no objectName and does author staticData); any one of the three alone validates; objectName: '' still parses (widening); a document with none of the three is refused ON THE REFINEMENT — exactly one issue, root path, RECORD_SOURCE_REQUIRED, all three key names in the message; .shape is still exposed on both objects (zod 4 attaches the check in place); gantt data is declared, not a passthrough hole.

Render half — BEFORE literals measured on origin/maind88e20f55 with the mirrors untouched, through the pin's OWN harness (a SchemaRendererProvider-wrapped bare SchemaRenderer, Date frozen). The harness is named because it matters twice over: a bare SchemaRenderer without the provider renders the error boundary for both components (4 elements, "failed to render") — an identity pin over that is the vacuous pin the ruling warns about — and the docs-gallery harness gives different absolute counts for the same tile.

  • gantt (examples/schema-catalog/test/objectql-record-source-render-identity-6939.test.tsx): 407 / 354 / 436 elements, a tag census, the visible text as a literal, and a SHA-256 of the full textContent (the chart's own style block is part of it). Identical before and after. Anti-vacuity: every authored task title on screen, no error box.
  • map (packages/plugin-map/src/ObjectMap.catalogRecordSource-6939.test.tsx): 16 / 14 / 14 elements, text = the geolocate glyph plus one marker glyph per row, tags in document order, 4 / 3 / 3 markers. Identical before and after. Anti-vacuity: one marker per staticData row, drawn on a LIVE maplibre stand-in (aria-label="Map" control). It lives in plugin-map because the same vi.mock('react-map-gl/maplibre') is measured INERT from examples/schema-catalog (the specifier does not resolve there; the real maplibre mounts and crashes on destroy).

Red-then-green ablation on the committed tree: restore both mirror sources to d88e20f55 (blob hashes checked equal to the base and unequal to HEAD; anchored LINE counts requireRecordSource 5 to 0, TS objectName?: string; 5 to 3, control ObjectMapSchema = BaseSchema.extend 1) → the three pins read 19 failed | 16 passed, exit 1 — every failure validator-facing; every render-identity, anti-vacuity and widening case green. Restore pinned to HEAD and proven by state: git diff HEAD empty, git status --porcelain empty, both blobs equal to HEAD, anchors back to 5 / 5.

Parity ledger

packages/types/src/__tests__/zod-mirror-parity.test.ts is UNTOUCHED. Measured before starting: neither objectql.zod.ts#ObjectMapSchema nor objectql.zod.ts#ObjectGanttSchema has an entry in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared; their only mention is the SPEC_DERIVED_PAIRS list, which is unaffected. Both faces moved together, so the pairs still record zero drift — asserted, not assumed: @object-ui/typestype-check (which compiles tsconfig.test.json) exit 0, and tsc -p tsconfig.test.json --listFiles lists the parity test, the new test and both edited sources (552 files in the program).

Verification (all at afcb85788, heavy runs serialized through the shared verify lock)

  • pnpm exec vitest run packages/types/ packages/plugin-map/ → Test Files 112 passed (112) / Tests 1662 passed (1662)
  • pnpm exec vitest run packages/plugin-gantt/ examples/schema-catalog/ → Test Files 81 passed (81) / Tests 2431 passed (2431)
  • the three pins alone → Test Files 3 passed / Tests 35 passed
  • type-check: @object-ui/types, @object-ui/plugin-map, @object-ui/plugin-gantt, @object-ui/example-schema-catalog — all exit 0 against a freshly built dependency closure (turbo build, 34 tasks)
  • lint for types / plugin-map / example-schema-catalog: 0 errors (267 + 141 pre-existing warnings, none in the new files)
  • gates, each exit 0 on its own verdict line: check:control-bytes, check:doc-fences, check:doc-types, check:doc-snippets (411 of 411 blocks compile), check:spec-symbols, check:vi-mock-specifiers, check:vi-mock-inherit, check:published-tsconfig-exclude, check:phantom-deps, type-check:coverage, changeset-presence, changeset-no-major
  • NOT MEASURED: check:readme-exports exits 1 only because plugin-ai's dist was not part of the built closure (3 unjudgeable self-imports; 403 judged, 0 fabricated); this diff edits no README and adds or removes no export.

Landing

Clause ② yes: this changes what a published mirror accepts. Labelled needs:contract-review, stays DRAFT, waits on the project director seat — not self-reviewed, not enqueued, no auto-merge. That is the expected end state.

Out of scope, filed separately: objectui#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true.

Session session_01EMrWaQw3XS5DxTHxp4yRyC.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…record-source refinement (objectui#6939, group 6 of 8)
Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName, a key
both renderers read THIRD: getDataConfig in plugin-map/src/ObjectMap.tsx and
plugin-gantt/src/ObjectGantt.tsx resolve records from data, then staticData, then
objectName. A document authored on staticData alone drew correctly and was refused
by safeValidateSchema — six catalog entries, three per component.
- objectName becomes optional on both mirrors and both TS twins in the same stroke.
- Each member ends in requireRecordSource: a superRefine whose issue sits at the root
path, carries params.code RECORD_SOURCE_REQUIRED and names data / staticData /
objectName. Presence is `!== undefined` (the ruling's wording), so the accept set
only widens: objectName alone, an empty one included, still parses.
- object-gantt additionally declares `data` (ViewDataSchema, the map's spelling): the
resolver's FIRST read was undeclared on both faces, which would have left the
refinement naming a key the validator had never heard of.
- Pins: validator contract in packages/types (six catalog entries validate; negative
arm checked by path / params.code / message), and render identity in each
renderer's own harness with BEFORE literals measured on d88e20f.
- Docs: the two plugin pages' Schema API blocks say which of the three is required.
Maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3179.2 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRlsZ8mv.js
StatusFAIL

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 halfVerdict
Aggregate closure ceiling✅ pass
Per-chunk ceilings❌ over its ceiling
Ceiling sensitivity (headroom)✅ pass
Ceiling freshness (checkout vs. base branch)✅ pass

📦 Bundle Size Report

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

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Seat bookkeeping — ⛔ NOT a contract review

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Clause ② yes, so this lane implements mechanically and ⛔ does not self-review, enqueue or auto-merge. needs:contract-review is on, the PR is draft, and it waits on the project director seat.

Verified on the branch by content, ⛔ not from the report

probeorigin/mainbranch afcb85788
objectName on the two ruled membersrequired at :584, :670optional at :640, :737, each with a describe naming the three sources
the other six objectName sites in the file66, unchanged
requireRecordSource / RECORD_SOURCE_REQUIREDabsentpresent, applied via .superRefine() on both members
⛔ FENCE — parity ledger, data-display.zod.ts, overlay.zod.ts, complex.zod.tsall four untouched
CONTROL — the diff reaches its targetobjectql.zod.ts +82, objectql.ts +47

The fence row is the one I care most about: five other groups of #6939 and three other in-flight PRs live in those files, and none of them moved.

⭐ The addition beyond the ruling's literal wording — correctly surfaced, and necessary

The ruling says only "objectName becomes optional with a refinement that at least one of data, staticData, objectName is present." The dev found that the gantt faces declared no data at all — the resolver's first read survived purely on BaseSchema's passthrough — and declared it, in the map pair's exact spelling. Measured: gantt data: declarations 2 → 3.

That is not scope creep, it is what makes the ruled refinement coherent: a refusal message naming data as an acceptable source, on a validator that had never heard of data, would tell authors to write a key the schema does not declare. ⇒ Ratified. The dev reported it rather than encoding it silently, which is the part that matters.

Two measurement details worth keeping

The vacuous pin was measured and discarded, not avoided by luck. A bare SchemaRenderer without the provider draws the error boundary for both components — 4 elements, "failed to render". Both tiles look identical before and after under that harness, so the render-identity claim would have passed for the wrong reason. The dev measured it first, threw it away, and states which harness the numbers are from.

A vi.mock was measured INERT.vi.mock('react-map-gl/maplibre') does not resolve from examples/schema-catalog, so real maplibre mounts and crashes; the map half therefore lives in packages/plugin-map. This repo has a check:vi-mock-specifiers gate for exactly that class, and the dev found the instance the gate would not have caught here.

The ablation split is the right shape: 19 red / 16 green, with every failure validator-facing and every render-identity, anti-vacuity and widening case green. Restore proven by state.

Filed, not acted on

#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true (plugin-map/src/index.tsx:67,77, plugin-gantt/src/index.tsx:107,117, restated in plugin-gantt/README.md:133) while the renderers read three record sources. Registrations were outside the ruling and are left as they stand. ⇒ After this PR the mirror and the designer input disagree; that is the follow-up's to reconcile, not this PR's to widen.

⚠️Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling. Measurement and path proof: #7399 comments 5522186641 / 5522268764, PR #7467 comment 5522177212.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ Correction to my own comment above — I called the Bundle Analysis red "not this PR's" without the proof, and this PR is not exempt

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Retracting one clause of 5522300143.

What I wrote:"Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling."

Why that was an overreach: the framework chunk is attributed by path/[\\/]packages[\\/](core|react|types)[\\/]/. That argument worked for PR #7467 because its entire file list is plugin-timeline and i18n, so it structurally cannot add a byte to the chunk. This PR edits packages/types/src/objectql.ts (+47) and packages/types/src/zod/objectql.zod.ts (+82) — squarely inside the group. I reused #7467's conclusion on a PR whose premise does not hold, and I did it one message after writing that "any not mine claim still needs the path-list proof."

The measurement, now read from the job log

framework 511.9 KB / 511.7 KB ceiling (OVER by 0.2 KB)
EAGER_CLOSURE_BASE_SHA: 0246d116e ← a NEWER main than #7467's c1fe272ad

Against #7467's reading on c1fe272ad512.8 KB, over by 1.1 KB — two things moved at once:

  1. main improved. Something landed between c1fe272ad and 0246d116e that took roughly 0.9 KB back out of the chunk. main is no longer 1.1 KB over.
  2. This PR adds packages/types bytes on top of whatever main now carries, and I cannot separate the two from this run alone.

Unresolved, not exonerated. The honest statement is: main at 0246d116e may or may not still be over on its own, and this PR's +129 lines of packages/types may or may not be what carries it past 511.7. Deciding that needs a main-alone measurement at 0246d116e, which did not exist when I wrote the earlier comment and does not exist yet.

I have redirected the running measurement to take it. Until it reports:

  • ⛔ This PR is not cleared of its byte cost, and my earlier sentence should not be read as clearing it.
  • ⛔ Still no ceiling raise, and still no re-run — the build is deterministic.
  • The PR is Clause ② yes and draft regardless, so nothing was going to land on the strength of that sentence. The correction matters for the record, not for an action I took.

⭐ The general rule this violates is the one I had just written into my own check-in: while main is over, the per-chunk verdict carries no per-PR information, so every "not mine" claim needs its own path-list proof. A PR that edits packages/types never had one available.

Everything else in 5522300143 — the by-content verification, the ratified data declaration, the fence readings — stands unchanged.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS (content); landing blocked by the per-chunk eager-closure ceiling, not by this diff's correctness

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head afcb85788. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6939 ruling row for this group (recorded 2026-09-02, batch #8): objectName optional on both mirrors and both TypeScript twins; each member ends in requireRecordSource, root-path issue with params.code = 'RECORD_SOURCE_REQUIRED', presence by !== undefined (the ruling's wording, not the renderers' truthiness), so the accept set only widens. The one addition beyond the ruling's literal text — object-gantt declaring data — is the right call and is reported: it is the FIRST key the resolver reads and was undeclared on both faces, so a refinement naming it would otherwise have referenced a key the validator had never heard of; it is spelled exactly as the map's. The pin carries both halves: six catalog entries validate; render identity measured on d88e20f55 through the pins' own harness (the vacuous-harness trap is named and avoided), anti-vacuity on both; the gantt data counter-probe shows it is declared, not a passthrough hole. .shape survives the refinement (zod 4 in-place), so the parity census is unaffected, measured.

Semver / changeset.@object-ui/typespatch: widening only — objectName alone, empty included, still parses; the one refused shape was refused before. Correct.

Boundary flags. No governed path; plugin-map/tsconfig.test.json gains node types for the fixture read (the shape two sibling packages already use).

Landing — blocked, and not by this PR. Every check is green except Bundle Analysis: BUDGET_CLOSURE_STATUS: pass (3179.2 KB against 3191.4 KB), BUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399 ("the framework per-chunk ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land"). This PR is now the third such fix. The director seat presents #7399 to the maintainer in the next decision batch; until it is ruled this PR stays draft with its carrier cleared (the review is complete), and the objectui seat re-runs Bundle Analysis after the ruling lands rather than shaving bytes to fit under a ceiling whose size is the open question.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3180.0 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-DMoCUVyD.js
StatusPASS

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


📦 Bundle Size Report

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

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:08
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Landing — the ceiling this was parked behind is fixed. Contract PASS stands; ready + auto-merge.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance for the release action: the in-seat contract-review route (维护者 2026-08-31 裁定 —— 交付后复核由派发席在席内完成,PASS ⇒ 同席剥标、ready、auto-merge).

The blocker named in the review is gone

The contract review (5522971250) returned PASS on content and blocked landing on one thing only:

Every check is green except Bundle AnalysisBUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399.

#7399 was ruled (maintainer, 2026-09-03, option A′) and landed as 177afeba1 (PR #7489). The framework chunk was 78.7 % @object-ui/i18n locale catalogueframework and infrastructure tied at priority 80 and framework was written first — so the ceiling that rejected this PR was never budgeting core|react|types at all. The catalogue now has its own i18n-locales chunk and both ceilings were re-baselined in the same change.

main was merged in, and Bundle Analysis on 29e117bd is now ✅ PASS (3180.0 KB / 3191.4 KB, 50 chunks) — 5524564830. ⛔ Not one byte of this PR's own diff was changed to achieve it, no ceiling was raised, no test touched.

The three pre-landing conditions, checked rather than assumed

#conditionreading
in-seat contract PASS on record5522971250 — director seat, at CONTRACT_REVIEW_TIER, fuse read claude-fable-5-1 against claude-fable-5
needs:contract-review cleared on both carriers✅ by hand — PR labels package: types / plugin / examples / tests; card #6939 labels pm:dispatched / priority:p2 / domain:ui. ⚠️ see the caveat below
all checks green by name (⛔ not the required subset)✅ 32/32 — 27 success, 5 skipped (dependabot, the two coverage jobs, …). Bundle Analysis, Type Check, Lint, all four Test (shard N/4), Governed Surface Queue Guard each read individually

⭐ Why re-review was not required, established mechanically rather than assumed

The review was performed at head afcb85788; the head is now 29e117bd. A moved head normally means the gate label goes back on. It does not here, and this was checked, not asserted: the second commit is a pure Merge branch 'main', and

git diff --stat afcb85788 29e117bd -- <the 9 files this branch changes> → empty

⇒ the reviewed contract increment is byte-identical at the landing head. The merge resolved nothing in this branch's own files.

⚠️ Recorded against the clean reading of ②

node scripts/pm/check-clause2-carriers.mjs --pair 7471 is the machine check for the dual-carrier condition. It could not run in this session — it exits 2 with "the pair could not be formed, so nothing about it was judged", because this seat reaches GitHub only through MCP and the script has no token of its own. Its own output says that is ⛔ not a clearance, so I am not claiming a machine reading: ② above is a manual verification of both label sets. Flagging it because a hand check is the weaker instrument and the next seat should know which one was used.

⚠️ And one correction against myself

I had this PR recorded as "at the director seat, Clause ② yes — do not chase", and left it sitting on that basis. That was wrong. The rule is 「交付后复核由派发席在席内完成……PASS ⇒ 同席剥标、ready、auto-merge」 — the director seat's role here is optional post-hoc audit, not the release action. The review had been complete and passing since 08:36; the PR then waited on my misreading rather than on anything real. It also means I nearly commissioned a duplicate contract review of an already-passed increment, which I stopped once I read this thread.

Part of #6939 is deliberately not a closing keyword — the card stays open until all eight groups land. Group 6 of 8.


Generated by Claude Code

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.

2 participants

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

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8) - #7471

Merged
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement
Sep 3, 2026
Merged

fix(types): object-map / object-gantt — objectName optional behind a record-source refinement (objectui#6939, group 6 of 8)#7471
os-project-manager merged 2 commits into
mainfrom
claude/issue-6939-objectql-source-refinement

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #6939 — group 6 of 8 (object-map + object-gantt), dispatched as its own PR per the maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8, comment 5510084784). ⛔ Deliberately not a closing keyword: the card stays open until all eight groups land.

The defect

Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName. Both renderers read their records from one of THREE keys, in this order — measured at the read sites, not taken from the card:

  • packages/plugin-map/src/ObjectMap.tsxgetDataConfig (lines 122-167): if (schema.data) … if (schema.staticData) … if (schema.objectName) … return null
  • packages/plugin-gantt/src/ObjectGantt.tsxgetDataConfig (lines 312-331): the same three keys, the same order

No fourth source on either; all three are read by both. A document authored on staticData alone draws correctly and was refused by safeValidateSchema — six catalog entries, three per component, all staticData-only.

The shape

  • objectName is optional on both mirrors and on both TypeScript twins (packages/types/src/objectql.ts), moved in the same stroke.
  • Each member ends in requireRecordSource — a superRefine following the in-repo shape of FormFieldSchema's refinement in form.zod.ts: issue at the ROOT path (no single key is at fault, and blaming objectName would re-teach the requiredness this removes), params.code = 'RECORD_SOURCE_REQUIRED', message naming the three keys an author can supply. Presence is !== undefined — the ruling's wording — not the renderers' truthiness, so the accept set only widens (the changeset carries the argument).
  • object-gantt additionally declares data (ViewDataSchema, the map's spelling) on both faces. It is the FIRST key that resolver reads and was declared on neither face — a refinement over it would have named a key the validator had never heard of. Reported here as the one addition beyond the ruling's literal wording; it is a declared-equals-enforced restoration of the kind the card is about, and the parity ledger is unaffected because both faces gain the same pairing the map pair already carries.
  • Docs: the Schema API blocks of content/docs/plugins/plugin-map.mdx (which still published objectName: string) and plugin-gantt.mdx now say which of the three is required.
  • Changeset: @object-ui/types patch — the accept set only widens toward what already renders.

The pin — both halves the ruling asks for

Validator half (packages/types/src/__tests__/objectql-record-source-refinement-6939.test.ts): the six catalog entries validate (control: each authors no objectName and does author staticData); any one of the three alone validates; objectName: '' still parses (widening); a document with none of the three is refused ON THE REFINEMENT — exactly one issue, root path, RECORD_SOURCE_REQUIRED, all three key names in the message; .shape is still exposed on both objects (zod 4 attaches the check in place); gantt data is declared, not a passthrough hole.

Render half — BEFORE literals measured on origin/maind88e20f55 with the mirrors untouched, through the pin's OWN harness (a SchemaRendererProvider-wrapped bare SchemaRenderer, Date frozen). The harness is named because it matters twice over: a bare SchemaRenderer without the provider renders the error boundary for both components (4 elements, "failed to render") — an identity pin over that is the vacuous pin the ruling warns about — and the docs-gallery harness gives different absolute counts for the same tile.

  • gantt (examples/schema-catalog/test/objectql-record-source-render-identity-6939.test.tsx): 407 / 354 / 436 elements, a tag census, the visible text as a literal, and a SHA-256 of the full textContent (the chart's own style block is part of it). Identical before and after. Anti-vacuity: every authored task title on screen, no error box.
  • map (packages/plugin-map/src/ObjectMap.catalogRecordSource-6939.test.tsx): 16 / 14 / 14 elements, text = the geolocate glyph plus one marker glyph per row, tags in document order, 4 / 3 / 3 markers. Identical before and after. Anti-vacuity: one marker per staticData row, drawn on a LIVE maplibre stand-in (aria-label="Map" control). It lives in plugin-map because the same vi.mock('react-map-gl/maplibre') is measured INERT from examples/schema-catalog (the specifier does not resolve there; the real maplibre mounts and crashes on destroy).

Red-then-green ablation on the committed tree: restore both mirror sources to d88e20f55 (blob hashes checked equal to the base and unequal to HEAD; anchored LINE counts requireRecordSource 5 to 0, TS objectName?: string; 5 to 3, control ObjectMapSchema = BaseSchema.extend 1) → the three pins read 19 failed | 16 passed, exit 1 — every failure validator-facing; every render-identity, anti-vacuity and widening case green. Restore pinned to HEAD and proven by state: git diff HEAD empty, git status --porcelain empty, both blobs equal to HEAD, anchors back to 5 / 5.

Parity ledger

packages/types/src/__tests__/zod-mirror-parity.test.ts is UNTOUCHED. Measured before starting: neither objectql.zod.ts#ObjectMapSchema nor objectql.zod.ts#ObjectGanttSchema has an entry in KnownDrift, UnmirroredDeclared or RuntimeOnlyDeclared; their only mention is the SPEC_DERIVED_PAIRS list, which is unaffected. Both faces moved together, so the pairs still record zero drift — asserted, not assumed: @object-ui/typestype-check (which compiles tsconfig.test.json) exit 0, and tsc -p tsconfig.test.json --listFiles lists the parity test, the new test and both edited sources (552 files in the program).

Verification (all at afcb85788, heavy runs serialized through the shared verify lock)

  • pnpm exec vitest run packages/types/ packages/plugin-map/ → Test Files 112 passed (112) / Tests 1662 passed (1662)
  • pnpm exec vitest run packages/plugin-gantt/ examples/schema-catalog/ → Test Files 81 passed (81) / Tests 2431 passed (2431)
  • the three pins alone → Test Files 3 passed / Tests 35 passed
  • type-check: @object-ui/types, @object-ui/plugin-map, @object-ui/plugin-gantt, @object-ui/example-schema-catalog — all exit 0 against a freshly built dependency closure (turbo build, 34 tasks)
  • lint for types / plugin-map / example-schema-catalog: 0 errors (267 + 141 pre-existing warnings, none in the new files)
  • gates, each exit 0 on its own verdict line: check:control-bytes, check:doc-fences, check:doc-types, check:doc-snippets (411 of 411 blocks compile), check:spec-symbols, check:vi-mock-specifiers, check:vi-mock-inherit, check:published-tsconfig-exclude, check:phantom-deps, type-check:coverage, changeset-presence, changeset-no-major
  • NOT MEASURED: check:readme-exports exits 1 only because plugin-ai's dist was not part of the built closure (3 unjudgeable self-imports; 403 judged, 0 fabricated); this diff edits no README and adds or removes no export.

Landing

Clause ② yes: this changes what a published mirror accepts. Labelled needs:contract-review, stays DRAFT, waits on the project director seat — not self-reviewed, not enqueued, no auto-merge. That is the expected end state.

Out of scope, filed separately: objectui#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true.

Session session_01EMrWaQw3XS5DxTHxp4yRyC.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

…record-source refinement (objectui#6939, group 6 of 8)
Both mirrors in packages/types/src/zod/objectql.zod.ts REQUIRED objectName, a key
both renderers read THIRD: getDataConfig in plugin-map/src/ObjectMap.tsx and
plugin-gantt/src/ObjectGantt.tsx resolve records from data, then staticData, then
objectName. A document authored on staticData alone drew correctly and was refused
by safeValidateSchema — six catalog entries, three per component.
- objectName becomes optional on both mirrors and both TS twins in the same stroke.
- Each member ends in requireRecordSource: a superRefine whose issue sits at the root
path, carries params.code RECORD_SOURCE_REQUIRED and names data / staticData /
objectName. Presence is `!== undefined` (the ruling's wording), so the accept set
only widens: objectName alone, an empty one included, still parses.
- object-gantt additionally declares `data` (ViewDataSchema, the map's spelling): the
resolver's FIRST read was undeclared on both faces, which would have left the
refinement naming a key the validator had never heard of.
- Pins: validator contract in packages/types (six catalog entries validate; negative
arm checked by path / params.code / message), and render identity in each
renderer's own harness with BEFORE literals measured on d88e20f.
- Docs: the two plugin pages' Schema API blocks say which of the three is required.
Maintainer ruling recorded 2026-09-02 (director seat, summon #8, decision batch #8).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC
@github-actions

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3179.2 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-BRlsZ8mv.js
StatusFAIL

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 halfVerdict
Aggregate closure ceiling✅ pass
Per-chunk ceilings❌ over its ceiling
Ceiling sensitivity (headroom)✅ pass
Ceiling freshness (checkout vs. base branch)✅ pass

📦 Bundle Size Report

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

Size Limits

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

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Seat bookkeeping — ⛔ NOT a contract review

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Clause ② yes, so this lane implements mechanically and ⛔ does not self-review, enqueue or auto-merge. needs:contract-review is on, the PR is draft, and it waits on the project director seat.

Verified on the branch by content, ⛔ not from the report

probeorigin/mainbranch afcb85788
objectName on the two ruled membersrequired at :584, :670optional at :640, :737, each with a describe naming the three sources
the other six objectName sites in the file66, unchanged
requireRecordSource / RECORD_SOURCE_REQUIREDabsentpresent, applied via .superRefine() on both members
⛔ FENCE — parity ledger, data-display.zod.ts, overlay.zod.ts, complex.zod.tsall four untouched
CONTROL — the diff reaches its targetobjectql.zod.ts +82, objectql.ts +47

The fence row is the one I care most about: five other groups of #6939 and three other in-flight PRs live in those files, and none of them moved.

⭐ The addition beyond the ruling's literal wording — correctly surfaced, and necessary

The ruling says only "objectName becomes optional with a refinement that at least one of data, staticData, objectName is present." The dev found that the gantt faces declared no data at all — the resolver's first read survived purely on BaseSchema's passthrough — and declared it, in the map pair's exact spelling. Measured: gantt data: declarations 2 → 3.

That is not scope creep, it is what makes the ruled refinement coherent: a refusal message naming data as an acceptable source, on a validator that had never heard of data, would tell authors to write a key the schema does not declare. ⇒ Ratified. The dev reported it rather than encoding it silently, which is the part that matters.

Two measurement details worth keeping

The vacuous pin was measured and discarded, not avoided by luck. A bare SchemaRenderer without the provider draws the error boundary for both components — 4 elements, "failed to render". Both tiles look identical before and after under that harness, so the render-identity claim would have passed for the wrong reason. The dev measured it first, threw it away, and states which harness the numbers are from.

A vi.mock was measured INERT.vi.mock('react-map-gl/maplibre') does not resolve from examples/schema-catalog, so real maplibre mounts and crashes; the map half therefore lives in packages/plugin-map. This repo has a check:vi-mock-specifiers gate for exactly that class, and the dev found the instance the gate would not have caught here.

The ablation split is the right shape: 19 red / 16 green, with every failure validator-facing and every render-identity, anti-vacuity and widening case green. Restore proven by state.

Filed, not acted on

#7470 — both plugin registrations' designer-facing inputs still declare objectNamerequired: true (plugin-map/src/index.tsx:67,77, plugin-gantt/src/index.tsx:107,117, restated in plugin-gantt/README.md:133) while the renderers read three record sources. Registrations were outside the ruling and are left as they stand. ⇒ After this PR the mirror and the designer input disagree; that is the follow-up's to reconcile, not this PR's to widen.

⚠️Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling. Measurement and path proof: #7399 comments 5522186641 / 5522268764, PR #7467 comment 5522177212.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

⛔ Correction to my own comment above — I called the Bundle Analysis red "not this PR's" without the proof, and this PR is not exempt

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Retracting one clause of 5522300143.

What I wrote:"Bundle Analysis red on this PR is not this PR'smain is over the framework per-chunk ceiling."

Why that was an overreach: the framework chunk is attributed by path/[\\/]packages[\\/](core|react|types)[\\/]/. That argument worked for PR #7467 because its entire file list is plugin-timeline and i18n, so it structurally cannot add a byte to the chunk. This PR edits packages/types/src/objectql.ts (+47) and packages/types/src/zod/objectql.zod.ts (+82) — squarely inside the group. I reused #7467's conclusion on a PR whose premise does not hold, and I did it one message after writing that "any not mine claim still needs the path-list proof."

The measurement, now read from the job log

framework 511.9 KB / 511.7 KB ceiling (OVER by 0.2 KB)
EAGER_CLOSURE_BASE_SHA: 0246d116e ← a NEWER main than #7467's c1fe272ad

Against #7467's reading on c1fe272ad512.8 KB, over by 1.1 KB — two things moved at once:

  1. main improved. Something landed between c1fe272ad and 0246d116e that took roughly 0.9 KB back out of the chunk. main is no longer 1.1 KB over.
  2. This PR adds packages/types bytes on top of whatever main now carries, and I cannot separate the two from this run alone.

Unresolved, not exonerated. The honest statement is: main at 0246d116e may or may not still be over on its own, and this PR's +129 lines of packages/types may or may not be what carries it past 511.7. Deciding that needs a main-alone measurement at 0246d116e, which did not exist when I wrote the earlier comment and does not exist yet.

I have redirected the running measurement to take it. Until it reports:

  • ⛔ This PR is not cleared of its byte cost, and my earlier sentence should not be read as clearing it.
  • ⛔ Still no ceiling raise, and still no re-run — the build is deterministic.
  • The PR is Clause ② yes and draft regardless, so nothing was going to land on the strength of that sentence. The correction matters for the record, not for an action I took.

⭐ The general rule this violates is the one I had just written into my own check-in: while main is over, the per-chunk verdict carries no per-PR information, so every "not mine" claim needs its own path-list proof. A PR that edits packages/types never had one available.

Everything else in 5522300143 — the by-content verification, the ratified data declaration, the fence readings — stands unchanged.


Generated by Claude Code

@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

Contract review — VERDICT: PASS (content); landing blocked by the per-chunk eager-closure ceiling, not by this diff's correctness

Implemented-by: objectui execution seat (session_01EMrWaQw3XS5DxTHxp4yRyC), head afcb85788. Reviewed-by: director seat, session session_01ShyhexkB2d1AeRZ85tgAAe, in-seat at the contract-review tier (fuse: claude-fable-5-1 against claude-fable-5, floor reading), 2026-09-03.

Derived judgments. Executes the #6939 ruling row for this group (recorded 2026-09-02, batch #8): objectName optional on both mirrors and both TypeScript twins; each member ends in requireRecordSource, root-path issue with params.code = 'RECORD_SOURCE_REQUIRED', presence by !== undefined (the ruling's wording, not the renderers' truthiness), so the accept set only widens. The one addition beyond the ruling's literal text — object-gantt declaring data — is the right call and is reported: it is the FIRST key the resolver reads and was undeclared on both faces, so a refinement naming it would otherwise have referenced a key the validator had never heard of; it is spelled exactly as the map's. The pin carries both halves: six catalog entries validate; render identity measured on d88e20f55 through the pins' own harness (the vacuous-harness trap is named and avoided), anti-vacuity on both; the gantt data counter-probe shows it is declared, not a passthrough hole. .shape survives the refinement (zod 4 in-place), so the parity census is unaffected, measured.

Semver / changeset.@object-ui/typespatch: widening only — objectName alone, empty included, still parses; the one refused shape was refused before. Correct.

Boundary flags. No governed path; plugin-map/tsconfig.test.json gains node types for the fixture read (the shape two sibling packages already use).

Landing — blocked, and not by this PR. Every check is green except Bundle Analysis: BUDGET_CLOSURE_STATUS: pass (3179.2 KB against 3191.4 KB), BUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399 ("the framework per-chunk ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land"). This PR is now the third such fix. The director seat presents #7399 to the maintainer in the next decision batch; until it is ruled this PR stays draft with its carrier cleared (the review is complete), and the objectui seat re-runs Bundle Analysis after the ruling lands rather than shaving bytes to fit under a ceiling whose size is the open question.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 50 chunks)3180.0 KB3191.4 KB
Main entry chunk (gzip)143.2 KB350 KB
Entry fileindex-DMoCUVyD.js
StatusPASS

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


📦 Bundle Size Report

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

Size Limits

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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:08
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
@os-project-managerClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Landing — the ceiling this was parked behind is fixed. Contract PASS stands; ready + auto-merge.

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Provenance for the release action: the in-seat contract-review route (维护者 2026-08-31 裁定 —— 交付后复核由派发席在席内完成,PASS ⇒ 同席剥标、ready、auto-merge).

The blocker named in the review is gone

The contract review (5522971250) returned PASS on content and blocked landing on one thing only:

Every check is green except Bundle AnalysisBUDGET_CLOSURE_CHUNK_STATUS: fail — the per-chunk eager-closure ceiling, the exact subject of decision card #7399.

#7399 was ruled (maintainer, 2026-09-03, option A′) and landed as 177afeba1 (PR #7489). The framework chunk was 78.7 % @object-ui/i18n locale catalogueframework and infrastructure tied at priority 80 and framework was written first — so the ceiling that rejected this PR was never budgeting core|react|types at all. The catalogue now has its own i18n-locales chunk and both ceilings were re-baselined in the same change.

main was merged in, and Bundle Analysis on 29e117bd is now ✅ PASS (3180.0 KB / 3191.4 KB, 50 chunks) — 5524564830. ⛔ Not one byte of this PR's own diff was changed to achieve it, no ceiling was raised, no test touched.

The three pre-landing conditions, checked rather than assumed

#conditionreading
in-seat contract PASS on record5522971250 — director seat, at CONTRACT_REVIEW_TIER, fuse read claude-fable-5-1 against claude-fable-5
needs:contract-review cleared on both carriers✅ by hand — PR labels package: types / plugin / examples / tests; card #6939 labels pm:dispatched / priority:p2 / domain:ui. ⚠️ see the caveat below
all checks green by name (⛔ not the required subset)✅ 32/32 — 27 success, 5 skipped (dependabot, the two coverage jobs, …). Bundle Analysis, Type Check, Lint, all four Test (shard N/4), Governed Surface Queue Guard each read individually

⭐ Why re-review was not required, established mechanically rather than assumed

The review was performed at head afcb85788; the head is now 29e117bd. A moved head normally means the gate label goes back on. It does not here, and this was checked, not asserted: the second commit is a pure Merge branch 'main', and

git diff --stat afcb85788 29e117bd -- <the 9 files this branch changes> → empty

⇒ the reviewed contract increment is byte-identical at the landing head. The merge resolved nothing in this branch's own files.

⚠️ Recorded against the clean reading of ②

node scripts/pm/check-clause2-carriers.mjs --pair 7471 is the machine check for the dual-carrier condition. It could not run in this session — it exits 2 with "the pair could not be formed, so nothing about it was judged", because this seat reaches GitHub only through MCP and the script has no token of its own. Its own output says that is ⛔ not a clearance, so I am not claiming a machine reading: ② above is a manual verification of both label sets. Flagging it because a hand check is the weaker instrument and the next seat should know which one was used.

⚠️ And one correction against myself

I had this PR recorded as "at the director seat, Clause ② yes — do not chase", and left it sitting on that basis. That was wrong. The rule is 「交付后复核由派发席在席内完成……PASS ⇒ 同席剥标、ready、auto-merge」 — the director seat's role here is optional post-hoc audit, not the release action. The review had been complete and passing since 08:36; the PR then waited on my misreading rather than on anything real. It also means I nearly commissioned a duplicate contract review of an already-passed increment, which I stopped once I read this thread.

Part of #6939 is deliberately not a closing keyword — the card stays open until all eight groups land. Group 6 of 8.


Generated by Claude Code

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.

2 participants

@os-project-manager@claude