Repository navigation
refactor(types): derive the published JoinedReportBlock from the installed spec (objectui#10940) - #11288
Conversation
…alled spec (objectui#10940) `@objectstack/spec` 17.5.0 types `JoinedReportBlockSchema`, so the hand-written interface that typed the legacy inline-query block is replaced by the spec's own `JoinedReportBlock` (the input shape), and `JoinedSpecReport.blocks` by the spec's own `Report.blocks`, made required. The parity test's JoinedReportBlock section drops the `OnTypedSpec` licence (the residual `erased` arm) and the local-vs-spec divergence probes, and pins the derivation plus the member-by-member change on the published type, each member lookup guarded against `never`. The `check:spec-symbols` ALLOW row for the symbol retires; the pending objectui#3162 changeset gets a dated note. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
…ation pin Mutual assignability cannot see an optional key dropped or added, and every block member but `name` is optional, so the derivation pin also compares the published and spec blocks' declared key sets. Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: Rendered 2026-09-30T23:51Z on PR objectui#11288 (card objectui#10940), from the PR body and file list, the net diff against ① Derived judgmentsTen judgments, each named right or wrong. None is wrong.
② Semver levelClause-②: yes
③ Boundary flagsDev flags (
Gates — the head's check-runs. Read at 2026-09-30T23:48Z, once the seven runs still in progress at a first read had completed ( Implemented-by: VERDICT: PASS |
Fixes #10940
Clause-②: yes
Why yes: the published
@object-ui/typesJoinedReportBlockchanges shape in both directions. The local-onlyobjectName/groupingsDown/groupingsAcross/filter/chartand the index signature leave (a narrowing). The spec-onlydataset/rows/values/runtimeFilter/orderarrive (a widening).What changed
packages/types/src/spec-report.ts. The hand-writteninterface JoinedReportBlockis gone.JoinedReportBlockis now the spec's ownJoinedReportBlockfrom@objectstack/spec/ui, which is the INPUT shape ofJoinedReportBlockSchema. It is imported under an alias and re-exported as a type alias, the same idiom the neighbouringSpecReport/SpecReportInputuse.JoinedSpecReport.blocksis nowNonNullable OF SpecReport['blocks'], the spec's ownReport.blocks, made required.report-chart-query-spec-parity.test.ts, theJoinedReportBlocksection.OnTypedSpeclicence is deleted. It was the lasterasedarm: on an erased spec it answeredtruefor every probe.JoinedSpecReport.blocksIS the spec's parsedReport.blocks) and the member-by-member change, asserted on the published type.[BlockMember OF …] extends [never] ? false : ….scripts/check-spec-symbol-derivation.mjs. The@object-ui/types:JoinedReportBlockALLOW row is deleted. The case-2b comment that pointed at that row now records the burn-down.spec-derived-unions.test.ts. The batch-8 ledger note that said "STILL erased" now records the burn-down..changeset/10940-joined-report-block-from-spec.md.@object-ui/typesminor, with the member-by-member change..changeset/3162-types-ledger-batch8-verdicts.md(pending). A dated, append-only note: in this same release, its "still erased" verdict for this symbol no longer holds. The frontmatter is byte-identical.Member by member (installed spec 17.5.0)
namestringstringtype'tabular' | 'summary' | 'matrix'tabularlabel,descriptionstring, or{ default, translations? }string, or an inline locale map (every value a string)columns{ field, label?, aggregate?, … }string[]: dimension names across a matrixobjectName,groupingsDown,groupingsAcross,filter,chartchartby name, objectstack#20161)[k: string]: unknowndataset,rows,values,runtimeFilter,orderTwo tier choices, and why
JoinedReportBlockis the INPUT tier. The spec exports this name with that meaning. A symbol with the spec's name but a different tier is the planted premise thatcheck:spec-symbolsexists to stop, and the rc.6 note inspec-report.tsrecords what a silent tier swap costs. The hand-written interface was input-shaped too (typeoptional), sotypestays optional.JoinedSpecReport.blocksis on the PARSED tier.JoinedSpecReportis built onSpecReport, which isReportParsed, and the spec'sReport.blocksalready carries the typed block on that tier. Soblocksderives fromReport.blocksby reference. Writingblocks: JoinedReportBlock[]would have intersected the input block with the parsed one thatSpecReportalready carries. Each parsed element can be assigned toJoinedReportBlock, but not the reverse; a pin asserts both directions.maintoday, with 17.5.0 installed,JoinedSpecReport['blocks'][number]is the old interface intersected with the spec's parsed block. For example, itscolumnsis{ field, … }[] & string[]. This change leaves one shape.Premises, re-measured on this worktree (base
e420df31)SpecTyping OF SpecJoinedReportBlocksatisfies 'typed'compiles on the unmodified tree: the typestype-checkexits 0. The TypeScript-API reading thatinstalledSpecTypingtakes gives: installed@objectstack/spec17.5.0,JoinedReportBlock: typed, and the lit controlReportSort: typed.e420df31the interface, the ALLOW row and the "Burnable now, on its own card" docblock were all present. But the bump (PR chore(deps): resolve @objectstack/* 17.5.0, and the zod 4.6.5 it requires, in pnpm-lock.yaml (objectui#11073) #11086, objectui#11073) had already cut the state pin down totypedalone. What remained of theerasedarm was theOnTypedSpeclicence, and this PR deletes it.git grepforJoinedReportBlock,JoinedSpecReportandisJoinedSpecReportoverpackages apps content examples scriptsfinds one hit outsidepackages/types: a comment inplugin-report/src/DatasetReportRenderer.tsx. That renderer types its blocks with its own localDatasetReportLike. Positive control: the grep hits insidepackages/types. No dependant's program names these symbols, so the downstreamtscsweep is a declared narrowing left to CI'sturbo run type-check. The reverse check below compiles through the published entry.The two items the contract review owed (the review comment on objectui#10940)
[BlockMember OF …] extends [never] ? false : …, andlabelanddescriptionare now separate probes. Ablation B below shows a dropped member turning its own probe red.erasedarm is deleted, not flipped.OnTypedSpecis gone, so no probe is licensed by an erased spec. The state pin admitstypedalone; that half landed at the bump.Verification, at
4806a1c16@object-ui/typestype-check(tsc --noEmit, examples, tests)vitest run packages/types/scripts/__tests__files that namecheck-spec-symbol-derivation)type-check:scriptspnpm --filter @object-ui/types buildcheck:spec-symbolscheck:installed-pin-claims,check:new-line-citations(0 new),check:control-bytescheck:changeset-claims,check:pending-changeset-literalscheck:test-path-roots,type-check:coveragecheck-changeset-presence,check-changeset-no-majorcheck-governed-queue-guard --testover the six pathseslint.config.jspopulation: all four came back linted.projectService,parserOptionsandtypeChecked), and no rule undereslint-rules/reads the disk. So this diff cannot move any other file's verdict.--no-inline-config, one error appears, at an untouched and sanctionedeslint-disableblock inspec-derived-unions.test.ts. objectui CI never passes that flag (zero hits in workflows and package scripts; the control is lit).Ablations. Each ran once, through objectstack's
scripts/ablation-replace.mjsin WRAP mode, which verifies the mutation on disk and proves the restore against the HEAD blob. None is kept as a test.tsc -p tsconfig.test.jsonexits 0, andcheck:spec-symbolsexits 0._publishedBlockIsTheSpecBlock,_joinedReportBlocksAreParsedTier,_legacyBlockKeysAreGone,_publishedBlockHasNoIndexSignature,_datasetBoundBlockKeysArrived,_blockColumnsAreDimensionNames,_blockLabelRefusesTheLegacyObjectArmand_blockDescriptionRefusesTheLegacyObjectArm.check:spec-symbolsexits 1: "a spec-named symbol is hand-written, not derived: interfaceJoinedReportBlock".labeldropped from the published block (anOmitof the spec type)._blockLabelRefusesTheLegacyObjectArm, on its own line. The other is_publishedBlockIsTheSpecBlock, and only through the declared-key-set comparison that4806a1c16added: mutual assignability alone stays green when an optional key is dropped.check:spec-symbolsexits 0, because anOmitof the spec type counts as derived.check:spec-symbolsexits 1: "@object-ui/types:JoinedReportBlockis in ALLOW but no longer collides with a spec export name". So the row's removal is enforced by the gate, not by prose.git diff HEADis empty, and porcelain is 0.Reverse check through the built declarations. This ran after the types build. A probe in
packages/plugin-report/srcimportsJoinedReportBlockfrom@object-ui/types; that package's tsconfig resolves@object-ui/typesthroughdist.--listFilesshows it readpackages/types/dist/spec-report.d.ts, not the source.objectNamefails with TS2353.--ignoreConfig. The probe file is removed.NOT MEASURED
check:spec-floorsandcheck:readme-exports. Both need every package'sdist, and this fresh worktree built only@object-ui/types. The floor gate reported 20 packagesno-artifact, and readme-exports reported "the population COLLAPSED -- this run proves nothing". Left to CI.@object-ui/types' dependants. This is a declared narrowing; see H3.Acceptance notes
3162changeset. The dispatch's pending-changeset clause sanctions it.git grep).content/docs/plugins/plugin-report.mdxalready documents joined blocks in the spec's dataset-bound shape.isJoinedSpecReportand every renderer are untouched.Dispatched by the
domain:spec @ objectuiseat. Session:https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT.Generated by Claude Code