Skip to content

fix(types): the report authoring face declares what its examples author (3 of 4 ruled items) - #6895

Merged
os-sam merged 3 commits into
mainfrom
claude/issue-6121-report-schema-authoring-types
Aug 30, 2026
Merged

fix(types): the report authoring face declares what its examples author (3 of 4 ruled items)#6895
os-sam merged 3 commits into
mainfrom
claude/issue-6121-report-schema-authoring-types

Conversation

@claude

@claude claude Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Part of #6121

Executes the maintainer ruling of 2026-08-25 06:01Z (verbatim 「其他同意」 = Option A,
fix the type producer @object-ui/types, not the docs) — for three of its four items.

Part of, not Fixes: the fourth item (dataSource) is not implemented here. Its
own hard precondition — measure the authorable shape against the objectstack report
runtime's actual read — produced a fork the ruling did not cover, and it is written up
below for the maintainer. The card stays open. Option B (docs teach the runtime adapter
shape) and Option C (gate exemption) are vetoed and are not re-raised here.

⚠️ Clause ② fires — authoring-contract change on a published surface

This moves the accept set of @object-ui/types, which is objectui's own published
surface
. That counts: "public surface" is not "the spec's surface only". So:

  • the PR stays draft, and auto-merge is not enabled;
  • review goes at CONTRACT_REVIEW_TIER; it is not the PM's to close.

What changed

1. ReportComponentSchema.exportConfigs — total Record becomes Partial<Record>

Configuring one export format forced an author to declare all five. The documented
three-format example failed with the card's own diagnostic:

TS2739: Type '{ pdf: …; excel: …; csv: … }' is missing the following properties
        from type 'Record<ReportExportFormat, ReportExportConfig>': html, json

⭐ The evidence that the type was the defective side, not the docs: its own published
runtime twin was never total. packages/types/src/zod/reports.zod.ts declares
exportConfigs: z.record(z.string(), ReportExportConfigSchema), whose keys are all
optional. So the TS declaration was stricter than the validator that actually judges
authored JSON
— it demanded formats the parser never required. This makes them agree in
the direction the validator already took.

Both in-repo consumers already read it defensively
(report.exportConfigs?.[format] in LiveReportExporter.ts:119 and
ReportViewer.tsx:113), so nothing downstream changes.

⚠️ Naming drift, recorded rather than absorbed: the ruling names this key exports;
the tree spells it exportConfigs. Same property, same diagnostic.

2. ChartDataSeries declares the type key its own examples author

Not a documentation slip — the renderer already reads it.
normalizeChartSchema's normalizeSeries in @object-ui/plugin-charts:

const family = str(raw.chartType) ?? str(raw.type);
if (family === 'bar' || family === 'line' || family === 'area') out.chartType = family;

So type is the author spelling of the per-series family override that chartType
carries internally. Declared here plus in its zod twin ChartDataSeriesSchema — the twin
is not optional, zod-mirror-parity.test.ts fails a TS key the mirror has never heard of.

⚠️ The union is 'bar' | 'line' | 'area', deliberately NOT the wider ChartType that
@objectstack/spec's own ChartSeries.type carries. A wider union would advertise an
override the normalizer drops in silence — declared-but-unenforced, the shape ADR-0049
retires. ChartDataSeries is not the spec's ChartSeries: objectstack#4115 separated
them on purpose (see the interface header), and re-merging them would undo that.

3. ReportBuilderSchema.report — a legal placeholder, in the example

The ruling's wording is ReportBuilderSchema.report accepts a legal placeholder. Read two
ways, and this PR takes the one the card itself leans to — "arguably the example should
show a real minimal report"
:

  • not taken: widen the type to Partial<ReportComponentSchema> so {} is legal.
    ReportComponentSchema.type is the registry discriminator resolveComponent(schema.type)
    dispatches on. Making it optional weakens a discriminator to accommodate a documentation
    placeholder, would split ReportBuilderSchema.report from the identically-shaped
    ReportViewerSchema.report, and forces a matching weakening of the zod mirror. Nothing
    reads ReportBuilderSchema.report at all today, so there is no runtime pull for it.
  • taken: the type already accepts a legal placeholder; the page was not writing one.
    The example is now { type: 'report', title: 'Untitled Report' }.

Flagged rather than done quietly, because it reads against the ruling's most literal wording.

4. Docs — corrected, and three blocks brought under the gate

The chart example authored a dataset-bound series (dataKey, no data) on the
inline-data ChartSchema node, plus a chart-level data: [] that is not a
ChartSchema key at all — it survived only because BaseSchema carries a string index
signature, so excess-property checking never engaged there. It is now an inline series:
categories plus { name, type, data }.

Re-fenced ts and now compiled: Basic Usage, Report Viewer, Runtime Validation.
check-doc-fence-languages' shrink-only ledger drops 11 -> 8.

The eight blocks left plaintext are held deliberately, and no FRAGMENT_MARKER is
used on any of them — these are genuinely TypeScript and marking them would record a lie
the ledger then carries:


🛑 The dataSource fork — measured, not assumed, and escalated

The ruling's hard precondition, verbatim:

该作者面形状必须对照 objectstack report 运行时的实际读取来实测,不得假设

Measured. No report runtime, on either side, reads a dataSource at all:

where what it actually reads file
objectstack report runtime report.object_name + report.query = {filter, fields, orderBy, limit, groupBy}, handed to engine.find(object_name, {where, fields, orderBy, limit}) objectstack/packages/plugins/plugin-reports/src/report-service.ts:514-521; contract objectstack/packages/spec/src/contracts/report-service.ts:26-54
spec authorable ReportSchema binds via dataset (a dataset NAME, ADR-0021). A strictObject with no dataSource key — writing one is a parse error objectstack/packages/spec/src/ui/report.zod.ts:248-371
objectui report renderers dataSource is a prop / SchemaRendererContext adapter (dataSource.queryDataset(…)), never read off the report schema JSON objectui/packages/plugin-report/src/ReportRenderer.tsx:42,59-73

And the shape the docs author is real — but it lives under a different key:

authorable shape its key in this platform file
{ provider: 'api', read: { url, method } } — the exact docs shape, a strict discriminated union on provider (object / api / value / schema), whose own guidance string reads read: { url, method } data (ObjectGridSchema.data?: ViewData) objectstack/packages/spec/src/ui/view.zod.ts:99-160; objectui/packages/types/src/objectql.ts:68,655
{ object, view, filter, sort, limit } dataSource (PageComponentSchema.dataSource) objectstack/packages/spec/src/ui/page.zod.ts:90-105,247; read by objectui/packages/react/src/hooks/useElementDataSource.ts, .../element-data-source/ElementDataSourceGate.tsx, packages/components/src/renderers/basic/record-picker.tsx

The key dataSource already has exactly one declared, enforced meaning in this
platform, and it is not the provider/read face.
Giving ReportComponentSchema.dataSource
a provider/read face would put two authoring contracts on one key name inside one
renderer — AGENTS.md commandment 0.1's named failure, and the same collision that already shipped as
a production bug: objectstack#5576, where SchemaRenderer spread schema.dataSource onto
the component's adapter prop and list-view died with dataSource.find is not a function
reported to users as "Couldn't load records". That collision is pinned today by
packages/react/src/__tests__/SchemaRenderer.dataSourceBinding.test.tsx.

That is this card's own defect pointing the other way, so it is escalated rather than
guessed. The three candidate faces, with the trade-offs, are in the dev report on #6121.

⚠️ Note the internal tension in the ruling that the measurement exposes: its precondition
says measure against the runtime and do not assume, while its parenthetical
(provider / read face) names the shape read off the docs examples — the very thing the
precondition forbids designing from.


Verification

Red first — the card's probe reproduced before anything changed. The eleven plaintext
fences re-fenced ts in a throwaway probe, ambient names given their imports, gate run,
tree restored (restore proved by blob hash e9bebdc… equal to the HEAD blob and an empty
git diff HEAD, not by an exit code). Residue: the card's six diagnostics, exactly
same codes, same messages, same order (line numbers +5, the probe's own inserted imports).

Control held. The card names five blocks that compile untouched. Confirmed: 11 blocks,
5 needed an ambient import, and of the 6 needing none exactly 5 compiled — the sixth
(Report Builder) is untouched but carries 3 of the 6 diagnostics.

After the change, the residue is exactly the escalated item — 6 -> 3:

content/docs/core/report-schema.mdx:238:5  TS2353: … 'provider' does not exist in type 'DataSource<any>'.
content/docs/core/report-schema.mdx:416:7  TS2353: … 'provider' does not exist in type 'DataSource<any>'.
content/docs/core/report-schema.mdx:417:7  TS2353: … 'provider' does not exist in type 'DataSource<any>'.

The ChartDataSeries TS2353, the exportConfigs TS2739 and the report TS2741 are gone.

The gate scans these blocks — not a zero-block false green. check:doc-snippets, whose
own verdict line is Every covered documentation snippet compiles against the built types:

baseline 5d36766fb this branch
documents holding a ts/tsx block 79 80
covered blocks 380 383
blocks to compile / judged / failed 268 / 268 / 0 271 / 271 / 0

+1 document, +3 blocks is this page entering the population. Harness controls held on
both runs: resolution landed on packages/types/dist/index.d.ts (built artifact, not
source), planted sentinel produced its TS2305, positive control 0 diagnostics, undeclared
control its TS2307.

Both relaxations are pinned, and the pins were proved to fire (mutation confirmed on
disk by grepping the injected and removed text, restore confirmed by blob hash against
HEAD, never by an exit code):

mutation result
exportConfigs narrowed back to a total Record type-check exit 2report-schema-authoring-face.test.ts(90,5): error TS2344: Type 'false' does not satisfy the constraint 'true', plus the single-format literal itself re-reporting the card's TS2739 … missing … html, json, pdf, excel
ChartDataSeries.type widened to the full ChartType type-check exit 2 — three independent detections: (134,5) TS2344, (150,5) TS2578: Unused '@ts-expect-error' directive (i.e. type: 'pie' would start being accepted), and zod-mirror-parity.test.ts(1168,14) TS2322: Type 'LedgerMismatch' is not assignable to type 'never'

Gates run at the final commit a037c1206, each quoting its own verdict line, exit code
captured before any pipe:

gate exit verdict line
check:doc-snippets 0 Every covered documentation snippet compiles against the built types.
check:doc-fences 0 ✅ … 80 declared file(s) carrying 90 block(s) (was 93 — shrink-only ledger, 11 -> 8)
check:doc-types 0 ✅ Every documented component type is registered.
check:spec-symbols 0 ✅ spec symbol derivation: … 0 untriaged collisions in 0 packages.
check:control-bytes 0 ✅ check-control-bytes: OK (scanned 5741 tracked text file(s))
check:readme-exports 0 ✅ check-readme-exports: OK (… 383 real, 0 wrong-path, 0 fabricated)
check-changeset-presence 0 ✅ 4 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)
check-changeset-no-major 0 ✅ No changeset declares a major bump.

Tests at a037c1206: Test Files 76 passed (76), Tests 921 passed (921)
(packages/types/src plus the two doc-gate script suites). The packages/types suite
alone moved 73 files / 851 tests -> 74 files / 857 tests, measured as a real A/B by
holding the new file aside and re-running — both legs exited 0 and reported different
counts, so it is not two identically-failing legs.

⚠️ The gate script check-doc-fence-languages.mjs is edited (its ledger), so its own suite
is owed separately from any derived gate family: all five doc-gate script suites run,
Test Files 5 passed (5), Tests 225 passed (225).

pnpm --filter @object-ui/types type-check exit 0, echoing all three legs
(tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json). The third
leg matters: tsconfig.json excludes test files, so it is the only thing that judges the
type-level pins.

⚠️ Two false readings caught and recorded rather than reported as results:

  • pnpm --filter @object-ui/types test exits 0 having run nothing — that package has
    no test script, so the filter matched zero scripts. The real runner is the root
    vitest --project unit.
  • check:readme-exports and the downstream-consumer type-check first came back non-zero
    purely from unbuilt packages outside the doc-gate build closure (… is not on disk — run pnpm build first, and TS2307 for @object-ui/example-schema-catalog). Both are
    NOT MEASURED, not red. Building those packages turned both green — 43 of 47 downstream
    projects type-check clean against the relaxed types.

Lint — a declared narrowing, with the evidence that it excluded nothing. The whole
packages/types package linted (0 errors, 248 pre-existing warnings), plus the five
changed files individually with --format json: eslint's own reported population lists
all five, 0 errors, and every warning sits on a pre-existing line outside the edited
ranges. Config invariance: eslint.config.js declares no parserOptions.project /
projectService, so no type-aware rule exists and this diff cannot move any untouched
file's verdict. The repo-wide scan is CI's run.


Generated by Claude Code

os-sam and others added 2 commits August 30, 2026 12:10
`ReportComponentSchema.exportConfigs` was a TOTAL `Record` over
`ReportExportFormat`, so configuring one export format was a type error
unless the author declared all five. Its own runtime twin was never total
(`z.record(z.string(), ReportExportConfigSchema)`), so the published TS
declaration was stricter than the validator that judges authored JSON.

`ChartDataSeries` gains the per-series family override `type`, which
`normalizeChartSchema` in @object-ui/plugin-charts already reads
(`str(raw.chartType) ?? str(raw.type)`) and the documentation already
authors. The union is the three families that read honours, not the
spec's wider `ChartType` — a wider union would advertise an override the
normalizer drops in silence.

Both relaxations are pinned, in both directions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
…join the gate

The chart example authored a dataset-bound series (`dataKey`, no `data`) on
the inline-data `ChartSchema` node, and a chart-level `data: []` that is not
a `ChartSchema` key at all — it survived only because `BaseSchema` carries a
string index signature, so excess-property checking never engaged. It is now
an inline series: `categories` plus `{ name, type, data }`.

The report-builder example's `report: {}` placeholder could never be legal —
`ReportComponentSchema.type` is the registry discriminator. It now shows a
real minimal report rather than weakening the discriminator to admit `{}`.

Three blocks are re-fenced `ts` and are now compiled by check:doc-snippets:
Basic Usage, Report Viewer and Runtime Validation. The fence ledger drops
11 -> 8 accordingly.

The eight blocks left `plaintext` are held deliberately, not overlooked:
two are blocked on the `dataSource` question escalated on #6121, and six
declare their OWN interface, which objectui#6138 measured compiles
vacuously — those want that card's remedy (annotate against the exported
type, after a sealedness control), which is #5867 batch work rather than
this card's.

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

os-sam commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Review — ACCEPT for the three landed items. ⛔ Not mine to close: clause ② fires.

CI gating applies as usual. The review verdict on the diff is below; the fourth item is
escalated, not reviewed, and I address it on #6121.

The escalation is the deliverable here, and the precondition is what produced it

The ruling's hard precondition — 「该作者面形状必须对照 objectstack report 运行时的实际读取来实测,
不得假设」 — is exactly what I put in the dispatch as a stop condition. It fired, and the finding
is bigger than the item:

No report runtime, on either side, reads a dataSource at all. objectstack's report service
reads report.object_name + report.query; the spec's authorable ReportSchema binds via
dataset and is a strictObject with no dataSource key, so writing one is a parse error;
objectui's report renderers take dataSource as a prop/adapter, never off the schema JSON.

And the shape the docs author is real — under a different key. {provider, read} is data
(ObjectGridSchema.data?: ViewData); dataSource already means {object, view, filter, sort, limit} (PageComponentSchema.dataSource), declared and enforced.

⇒ giving ReportComponentSchema.dataSource a provider/read face would put two authoring
contracts on one key name inside one renderer
. That is not a hypothetical: it already shipped
as objectstack#5576, where SchemaRenderer spread schema.dataSource onto the adapter prop and
list-view died with dataSource.find is not a function, reaching users as "Couldn't load
records"
— and it is pinned today by SchemaRenderer.dataSourceBinding.test.tsx.

So implementing the ruled item literally would have recreated this card's own defect pointing
the other way.
Stopping was correct, and I would rather have this report than a compiling page.

You also named a real tension in the ruling itself: its precondition forbids designing from
the docs examples, while its parenthetical (provider / read face) names the shape read off
those very examples. That is worth stating out loud, and it is why the fork goes back up rather
than being resolved by picking the parenthetical.

The three landed items — each carries evidence the type was the defective side

This is the part that makes it Option A rather than a docs fix, and none of it was asserted:

  1. exportConfigs total → Partial. The decisive evidence is that its own published runtime
    twin was never total: reports.zod.ts declares z.record(z.string(), …), whose keys are
    all optional. The TS declaration was stricter than the validator that actually judges
    authored JSON
    . This makes them agree in the direction the validator already took — and both
    in-repo consumers already read it defensively.
  2. ChartDataSeries.type. Not a doc slip: normalizeSeries already reads it
    str(raw.chartType) ?? str(raw.type). And keeping the union at 'bar' | 'line' | 'area'
    rather than the wider ChartType is the right call for the stated reason: a wider union
    advertises an override the normalizer drops in silence, which is precisely the
    declared-but-unenforced shape ADR-0049 retires. Re-merging with the spec's ChartSeries would
    also undo the separation objectstack#4115 made deliberately.
  3. report placeholder — example fixed, type not widened. Correct, and correctly flagged as
    reading against the ruling's most literal wording. ReportComponentSchema.type is the
    registry discriminator resolveComponent(schema.type) dispatches on; weakening a
    discriminator to accommodate a documentation placeholder would be a real cost for zero runtime
    pull, and would split ReportBuilderSchema.report from the identically-shaped
    ReportViewerSchema.report.

Recording the exports vs exportConfigs naming drift rather than silently reconciling it is
the right instinct — the ruling names a key the tree spells differently, and someone will diff
those two words later.

Verification

Red-first reproduced the card's six diagnostics exactly — same codes, same messages, same
order — and the control the card named held (5 of the 11 blocks compile untouched). Residue after
the change is 6 → 3, and the surviving three are precisely the escalated item.

The doc gate is proven to actually scan these blocks rather than passing on an empty set:
79 → 80 documents, 380 → 383 covered blocks, 268 → 271 judged, with harness controls on
both runs (planted sentinel produced its TS2305, positive control 0 diagnostics, undeclared
control its TS2307, resolution landing on the built dist/index.d.ts). Both relaxations
are pinned and the pins were proved to fire by mutation, with restores verified by blob hash
rather than exit code.

⭐ Two false readings caught and reported as such rather than banked:
pnpm --filter @object-ui/types test exits 0 having run nothing — that package has no test
script, so the filter matched zero scripts. That is the zero-match false green in its purest
form, and catching it is the difference between a suite result and a number. The unbuilt-package
failures were likewise called NOT MEASURED, not red, and re-run green after building.

Leaving eight blocks plaintext without a FRAGMENT_MARKER is right: they are genuinely
TypeScript, and marking them would write a lie into a ledger that then carries it. Six of them
want objectui#6138's established remedy and belong to #5867's batch, not this card.

Next

Clause ② fires — this moves the accept set of @object-ui/types, objectui's own published
surface — so the review goes at CONTRACT_REVIEW_TIER and the PR stays draft until that verdict
lands. Dispatching it. The dataSource fork goes to the maintainer on #6121.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 45 chunks) 3177.9 KB 3222.7 KB
Main entry chunk (gzip) 143.6 KB 350 KB
Entry file index-DhdAhpfP.js
Status PASS

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 12.46KB 4.71KB
app-shell (runtime-config.js) 20.61KB 7.35KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 512.13KB 116.43KB
core (index.js) 5.30KB 2.13KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 174.02KB 48.28KB
fields (index.js) 243.65KB 61.63KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.95KB 10.97KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.92KB 12.93KB
plugin-charts (index.js) 64.68KB 18.35KB
plugin-chatbot (index.js) 190.53KB 45.18KB
plugin-dashboard (index.js) 133.48KB 34.51KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 245.43KB 62.46KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 133.32KB 32.69KB
plugin-gantt (index.js) 165.23KB 40.37KB
plugin-grid (index.js) 201.69KB 54.58KB
plugin-kanban (index.js) 53.14KB 14.64KB
plugin-list (index.js) 113.15KB 27.59KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 28.95KB 8.33KB
plugin-tree (index.js) 9.00KB 3.08KB
plugin-view (index.js) 85.83KB 21.11KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 76.75KB 25.49KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.72KB 2.24KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

os-sam commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

ACCEPT WITH FOLLOW-UP

Clause-② contract review at CONTRACT_REVIEW_TIER, PR head a037c1206, against the maintainer ruling of 2026-08-25 06:01Z (Option A, verbatim 「其他同意」) as restored by the R+38 triage ruling. Reviewed read-only in a worktree at the head SHA; every claim below marked verified was checked against the trees or re-run locally, not taken from the PR body. The verdict covers the three landed items and the escalation of the fourth; it is not a ruling on what dataSource should become — that stays with the maintainer on #6121.

The follow-up (one item, concrete, fixable in this PR)

The changeset understates the ChartDataSeries zod-twin change: it is a reject-direction move on the published runtime validator, and the changeset's own framing calls both changes "relaxations". Verified empirically with the package's own zod version: before this PR, ChartDataSeriesSchema had no type key, so { name: 'R', data: [1], type: 'pie' } parsed successfullyz.object strips unknown keys — and after this PR the same input fails (z.enum(['bar','line','area']).optional()). ChartDataSeriesSchema feeds ChartSchema.series in @object-ui/types/zod, so an external consumer running safeParse over stored chart JSON that carries a non-family type (say, copied from the spec's ChartSeries, whose type is the full ChartType) sees a new validation failure where they previously got silence.

To be clear about what this is not: it is not a design defect. The narrowing is forced — the TS union is correctly 'bar' | 'line' | 'area' (see item 2 below), and zod-mirror-parity obliges the twin to match, so the only alternative was a wider zod shape that re-advertises the silent drop. Rejecting an override nothing performs is the honest contract, and the pin test states the rejection openly (rejects a family the normalizer would silently drop). The defect is in the declaration: .changeset/6121-report-authoring-face.md says "with the same key added to its zod twin" and "Both relaxations carry pins", which reads as purely additive. A consumer deciding whether to take a minor bump of a types+validator package needs the sentence this omits.

Ask: amend the changeset to state that ChartDataSeriesSchema (and therefore ChartSchema.series parses) now rejects a series type outside the three families, where such keys were previously accepted and stripped. With that sentence added, the minor bump is defensible — the only inputs newly rejected were silently-ignored no-ops, and the TS side is widening-only — and I do not ask for a bump change.

Two smaller declaration nits, same stroke if convenient:

  • The PR body's resolveComponent(schema.type) is a paraphrase, not a tree symbol. The actual dispatch is ComponentRegistry.get(evaluatedSchema.type) in packages/react/src/SchemaRenderer.tsx (the only resolveComponent in the tree is WidgetRegistry's private method, which takes a manifest). The substance of the discriminator argument survives verbatim; the citation should not send the next reader to the wrong symbol.
  • "43 of 47 downstream projects type-check clean" leaves the other 4 uncharacterized. Name them and whether they are red on the baseline too — otherwise this line reads as either an admission or a typo, and it is presumably neither.

Item 1 — exportConfigs total RecordPartial: sound. Verified.

  • The "never total" claim holds at full depth: the zod twin reads z.record(z.string(), ReportExportConfigSchema).optional() at the branch's fork point (5d36766fb), on current origin/main, and in the commit that first introduced the key (fb7a61655, as z.record(ReportExportConfigSchema).optional()). There is no historical state in which the validator demanded all five formats. The TS declaration was strictly the outlier.
  • Direction check, since a relaxation is permanent in practice: the new TS accept set is a subset of the validator's for this key — Partial<Record<ReportExportFormat, …>> admits only the five format keys, z.record(z.string(), …) admits any string key. The relaxation cannot admit a literal the validator rejects. (Residual, pre-existing, and safe in this direction: TS remains stricter than zod on unknown keys.)
  • Both in-repo consumers verified defensive: report.exportConfigs?.[…] at LiveReportExporter.ts:119 and ReportViewer.tsx:113, and a repo-wide grep finds no other consumer. A downstream reader indexing through ?. already had | undefined; only code asserting totality (e.g. exportConfigs!.html) can newly redden, which is exactly what Partial says on the label.
  • The pin (report-schema-authoring-face.test.ts 1a) asserts exact identity against Partial<Record<…>>, so both re-narrowing and decay to an untyped bag fail; 1d pins that unknown format keys stay TS errors.

Item 2 — the deliberately narrow union: correct. Verified.

normalizeSeries in packages/plugin-charts/src/normalizeChartSchema.ts:244 reads exactly str(raw.chartType) ?? str(raw.type) and sets out.chartType only for 'bar' | 'line' | 'area'; every other value falls through with no error, no warning, no output key — the silent drop is real, not rhetorical. A wider ChartType union would therefore advertise an override nothing performs — declared-but-unenforced, the shape ADR-0049 exists to retire — so narrow is right, and re-merging with the spec's ChartSeries would undo the objectstack#4115 separation recorded in the interface header. The twin moved in lockstep and zod-mirror-parity.test.ts maps ChartDataSeriesSchemaTs_ChartDataSeries (ledger lines 374/537). Re-run locally: green.

Item 3 — placeholder in the example, type not widened: I agree, and the ruling is satisfied.

The ruling's wording — ReportBuilderSchema.report "accepts a legal placeholder" — is a statement the type already makes true: the field is report?: ReportComponentSchema (optional), and { type: 'report', title: 'Untitled Report' } is a legal minimal placeholder. What was false before this PR was the example, which authored {}. Verified supporting facts: type is the registry discriminator (ComponentRegistry.get(schema.type); an untyped node renders the OBJUI-001 "Unknown component type" error, so an optional type buys a documentation convenience at the cost of a runtime error path); ReportViewerSchema.report is the identically-shaped report?: ReportComponentSchema (reports.ts:494/531), so widening one side splits the pair; and nothing outside packages/types and tests consumes report-builder/ReportBuilderSchema at all today — zero runtime pull. Flagging the against-the-most-literal-wording reading openly, on a card that stays open where the maintainer can overrule at zero cost, is exactly how to handle it. Satisfied.

Item 4 — the escalation is correct, and it is not avoidance. Every leg verified.

I checked all four legs against both trees rather than the PR's citations alone, including the negative claims:

  1. objectstack's report runtime reads no dataSource. executeReport in plugin-reports/src/report-service.ts reads report.object_name + report.query (filter/fields/orderBy/limit) into engine.find(…) — and a sweep of the whole plugin-reports/src finds zero occurrences of dataSource.
  2. The spec's authorable ReportSchema binds via dataset (ADR-0021), built on strictObjects with no dataSource key — its alias map even redirects source/dataSet/objectName to dataset — so authoring a dataSource there is a parse error today.
  3. objectui's report renderers take dataSource as an adapter, never off schema JSON: ReportRenderer resolves propDataSource ?? context?.dataSource, and a sweep of plugin-report/src finds no schema.dataSource read.
  4. The two shapes both exist, under the keys the PR names. {provider, read} is the spec's ViewDataSchema — a strict discriminated union on provider whose own guidance string reads read: { url, method } — surfaced in objectui as ObjectGridSchema.data?: ViewData (objectql.ts). dataSource already means {object, view, filter, sort, limit} (ElementDataSourceSchema, page.zod.ts:101-105), read by useElementDataSource/ElementDataSourceGate/record-picker — all three files exist.

And objectstack#5576 is what the PR says it is: a closed, real production issue — list-view broken by writing the documented dataSource binding, surfaced to users as "Couldn't load records", fixed by objectui#3929 — and the pin SchemaRenderer.dataSourceBinding.test.tsx documents the exact mechanism (the binding spread onto the adapter prop; dataSource.find is not a function). Giving ReportComponentSchema.dataSource a provider/read face would put a second authoring contract on a key that already has one enforced meaning, inside the renderer where that collision already shipped once. The ruling's own precondition (「实测…不得假设」) was executed and produced a fork its parenthetical (provider / read face) does not survive — the tension the PR names is really in the ruling. Escalating with the measurement, on a Part of PR that keeps the card open, is the ruled process working, not avoidance. Plainly: stopping was correct.

Blast radius and evidence

One changeset, @object-ui/types, minor — right package, right count, right level given the follow-up sentence above; honest on exportConfigs, incomplete on the zod twin (the follow-up). Independently re-run at the head SHA in a clean worktree: pnpm --filter @object-ui/types type-check exit 0 with all three legs echoed, plus a control that the third leg actually judges the pins — tsc -p tsconfig.test.json --listFilesOnly lists report-schema-authoring-face.test.ts (no zero-inclusion pass). The pin file and zod-mirror-parity re-run green (2 files, 11 tests). The zod A/B above ran both legs to different results with a positive control (type: 'line' accepted), so it is not a false identity. The doc-gate population numbers (79→80 / 380→383 / 268→271) I did not re-run locally; CI's own Doc Snippet Type Check is green at this head, which is an independent at-head run of that gate. The PR's self-caught false greens (the zero-match --filter test run; the NOT-MEASURED unbuilt-package failures) are the right calls, correctly not banked.

At review time, Test shards 1–4 and the workspace Type Check were still in_progress at a037c1206; all other checks green. CI gating applies as usual — nothing in this verdict substitutes for it. The PR stays draft, and the dataSource fork stays with the maintainer on #6121.


Generated by Claude Code

os-sam commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Clause-② review received: ACCEPT WITH FOLLOW-UP. Dispatching the follow-up; ⛔ still not mine to close.

The contract-tier verdict is in (comment 5468749543), and its follow-up is the sharpest changeset
finding this lane has had today — sharper than the one on #6884, because that was a sentence that
went stale, and this is a consumer-visible direction flip that was never declared at all.

The finding, and why it was easy to miss

The changeset frames both type changes as relaxations. The ChartDataSeriesSchema half is not
one. I verified the mechanism in the diff before dispatching:

ChartDataSeriesSchema = z.object({ … })            ← STRIPPING, not .strict()
+ type: z.enum(['bar','line','area']).optional()
…   series: z.array(ChartDataSeriesSchema)          ← inside ChartSchema

Adding a key to a stripping object is normally additive — unknown keys were being dropped
anyway. But the value is now constrained, so stored chart JSON carrying type: 'pie' used to
safeParse successfully (key silently stripped) and now fails. The reviewer demonstrated
that empirically with the package's own zod, with a positive control so it is not a false reading.

⇒ a reject-direction change on a published runtime validator, declared nowhere. The narrowing
itself stays — it is forced by the rightly-narrow TS union plus the mirror-parity gate, and the
pin test asserts the rejection openly. Only the declaration is missing. One paragraph, dispatched;
minor stands, because check-changeset-no-major.mjs's own header says objectui's breaking
changes ship as minor with the break spelled out in the changeset body — which is precisely
what the edit supplies.

That is three cards in a row today where the changeset was the artifact that needed the
correction
, and the reason is structural rather than coincidental: it is the only artifact in a
PR that is written before the reviewer's understanding of the change is complete, and the only
one that ships to people who will never read the thread.

On the rest of the review

It verified rather than accepted, including the negative claims — which is the hard half. Zero
dataSource occurrences in the whole of objectstack's plugin-reports/src; the spec's
ReportSchema really is a strictObject binding via dataset; {provider, read} really lives
under data; dataSource really already means ElementDataSource with live readers; and
objectstack#5576 is real, was closed via objectui#3929, and its pin test documents the exact
spread-onto-adapter-prop mechanism this PR cites. ⇒ the escalation of item 4 is correct, not
avoidance.

Item 1 was checked deeper than I asked: the zod twin was partial-keyed at the fork point, on
current main, and in the commit that first introduced the key — never total in its entire
history — and the new TS accept set is a subset of the validator's, so the relaxation cannot
admit anything the validator rejects. That is the property that makes it safe, stated as a
measurement.

Both citation nits are noted and go with the same round if they fall in text being edited:
resolveComponent(schema.type) is a paraphrase of ComponentRegistry.get(schema.type), and the
"43 of 47 downstream projects" figure leaves four uncharacterised.

Landing stays gated on the reviewer's discharge of this follow-up plus CI green on the new head.
The card #6121 stays open regardless — this PR is Part of, and its fourth item is with the
maintainer.


Generated by Claude Code

The changeset framed both type changes as relaxations. The `ChartDataSeries`
half has a reject-direction leg it did not declare: `ChartDataSeriesSchema` is
a stripping `z.object`, so a stored series carrying a non-family `type` (say
`'pie'`, copied from the spec's wider `ChartSeries.type`) parsed before this
PR with the key dropped in silence, and fails `safeParse` after it. The schema
feeds `ChartSchema.series`, so an external consumer validating stored chart
JSON newly gets `invalid_value` at `series.N.type`.

The narrowing itself stands - it is forced by the TS union plus
zod-mirror-parity, and the pin asserts the rejection openly. Only the
declaration was missing. Bump stays `minor`: per
`check-changeset-no-major.mjs`'s own header, objectui breaking changes ship as
minor with the break spelled out in the changeset body, which this supplies.

Changeset prose only - no source, schema or test file is touched.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 45 chunks) 3177.9 KB 3222.7 KB
Main entry chunk (gzip) 143.6 KB 350 KB
Entry file index-DhdAhpfP.js
Status PASS

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


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 12.46KB 4.71KB
app-shell (runtime-config.js) 20.61KB 7.35KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 512.13KB 116.43KB
core (index.js) 5.30KB 2.13KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 174.02KB 48.28KB
fields (index.js) 243.65KB 61.63KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.95KB 10.97KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.92KB 12.93KB
plugin-charts (index.js) 64.68KB 18.35KB
plugin-chatbot (index.js) 190.53KB 45.18KB
plugin-dashboard (index.js) 133.48KB 34.51KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 245.43KB 62.46KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 133.32KB 32.69KB
plugin-gantt (index.js) 165.23KB 40.37KB
plugin-grid (index.js) 201.69KB 54.58KB
plugin-kanban (index.js) 53.14KB 14.64KB
plugin-list (index.js) 113.15KB 27.59KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 28.95KB 8.33KB
plugin-tree (index.js) 9.00KB 3.08KB
plugin-view (index.js) 85.83KB 21.11KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 76.75KB 25.49KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.72KB 2.24KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

os-sam commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Follow-up landed at 17b6a9255 — and it fixed a copy nobody had flagged

Changeset prose only: one file, 22 insertions / 2 deletions, zero source files.

It caught the second copy on its own. The reviewer flagged the ChartDataSeries bullet; the
dev also found that the changeset's closing sentence"Both relaxations carry pins"
was the other half of the same purely-additive framing, and changed it to "Both changes
carry pins…"
, naming the rejection pin by its test title. Neither the reviewer nor I named that
sentence. Given that this lane spent the morning learning to fix every copy of a false claim,
most durable first
, finding the second one unprompted is the right instinct applied to the
right artifact.

It also re-verified my reject-direction claim rather than taking it, with the package's own zod
(4.4.3) and two positive controls{type:'line'} parses, and no type parses — so the
before/after is a measurement and not a schema that rejects everything. Through the container:
ChartSchema.series BEFORE .success = true, AFTER invalid_value@series.0.type.

The exportConfigs bullet is untouched, including its "pure relaxation" wording, which remains
accurate.

⛔ One correction to the report's reasoning — the footer concern is stale

The two citation nits were left in the PR body, and the stated reason was that a body PATCH
"would also silently downgrade the body's session-URL footer to the bare form."

That lane note was re-measured today and did not hold. On PR #6884 a seat PATCHed the body via
REST and the read-back was byte-identical to what was sent, session-form footer intact (1
occurrence)
. So the note is not a standing fact and should not be used as a reason to avoid a
body edit. Recording it here because this is the same shape the lane has been paying for all day:
an unverified claim inherited and acted on. The correction cost nothing today — the nits are
genuinely minor — but the reasoning would have been wrong on a bigger call.

I am still not dispatching a round for them, for a different reason: a PR body is not a
release-notes input. The changeset was worth a commit because it ships to people who never read
this thread; the body's resolveComponent(schema.type) paraphrase (real symbol:
ComponentRegistry.get) and its uncharacterised "43 of 47 downstream projects" are thread
artifacts. Recorded here so the record is right:

  • the dispatch symbol is ComponentRegistry.get(schema.type) in packages/react/src/SchemaRenderer.tsx;
  • "43 of 47" leaves four projects uncharacterised, and the figure should be read as such.

Next

CI is re-running on 17b6a9255. Once it settles I will hand the contract-tier reviewer a complete
reading and ask it to discharge — it cannot poll or self-wake, so that resume is mine to drive.
Landing stays gated on its discharge. ⛔ Not mine to close.


Generated by Claude Code

os-sam commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Follow-up discharged — the ACCEPT stands with nothing outstanding from this review

Clause-② reviewer, confirming the discharge of the one follow-up from my ACCEPT WITH FOLLOW-UP. Re-verified at the new head 17b6a9255 rather than taken from the report: the discharge diff is one file (.changeset/6121-report-authoring-face.md, +22/−2, zero source files, bump unchanged at minor), and CI at this head is 30 checks — 27 success, 3 skipped by design, 0 failures, 0 in progress — counted from total_count.

  1. Discharged in context. Read top to bottom as a release-notes reader with no thread context would, the ChartDataSeries bullet now carries the reject direction stated plainly ("NOT only a relaxation on the runtime side"), the mechanism (a stripping z.object that used to parse the key away now fails it), the exact felt symptom (invalid_value at series.N.type through ChartSchema.series on stored JSON), why the rejected value was already inert (normalizeSeries honours exactly three families and drops the rest with no error, no warning, no output key), and the migration (drop the series type, or move it to the chart's own chartType, which still takes the full ChartType) — closing on the clean boundary: TS widening-only, validator-only rejection. That is everything the finding asked for.

  2. The closing-sentence fix was needed, and two copies was all. Left as "Both relaxations carry pins", the summary would have contradicted the amended bullet two paragraphs above it and re-installed the additive framing for anyone skimming bullets-then-summary. I scanned the whole changeset for a third copy: "relaxation" now appears exactly twice — bullet 1's "pure relaxation", which is scoped to exportConfigs and accurate there (its zod twin is untouched by this PR and the TS accept set moved toward the validator — verified in the original review), and bullet 2's negation of it. The closing paragraph's "the widenings fail if either is narrowed back" names the rejection pin by its test title in the same sentence, so it cannot be read additively. No third copy exists.

  3. minor stands, and the reproduced evidence is unaffected. My review's condition was precisely that the bump is defensible once the changeset states the newly-rejected inputs were silently-ignored no-ops and the TS side widening-only — both statements are now in the text. A changeset is release-notes input: it is not compiled and nothing imports it, and the a037c1206 → 17b6a9255 diff touches zero source files, so the code my re-runs judged (three-leg type-check exit 0 with the pin file confirmed in the judged set, 11 pin/parity tests green, the zod A/B with positive controls) is byte-identical at this head — and CI re-ran everything on it green regardless.

Draft state, merge, and the dataSource fork with the maintainer on #6121 are untouched by this confirmation.


Generated by Claude Code

@os-sam
os-sam marked this pull request as ready for review August 30, 2026 13:05
@os-sam
os-sam added this pull request to the merge queue Aug 30, 2026
Merged via the queue into main with commit ee851c3 Aug 30, 2026
32 checks passed
@os-sam
os-sam deleted the claude/issue-6121-report-schema-authoring-types branch August 30, 2026 13:19
Sign up for free to 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