fix(types,plugin-dashboard)!: object-pivot and object-data-table refuse the DrillDownConfig members they never read (objectui#10685) - #10710
Conversation
…ts prop type `object-pivot` takes its own drill shape, `ObjectPivotDrillDownConfig`: the shared `DrillDownConfig` with `mode?: never`. Every pivot click point is an aggregated bucket, so the block always drills through and never read `mode`. The `DrillDownConfig.mode` docblock now names only the block that reads the key, `object-data-table`. Pins, compile-time: the per-block shapes for object-pivot and object-chart in @object-ui/types, and ObjectPivotTable's schema.drillDown prop in @object-ui/plugin-dashboard. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…ntry's mode reading Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
|
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
|
…port and target navigate on both doors `ObjectDataTableSchema.drillDown` takes the block's own shape, `ObjectDataTableDrillDownConfig`: the shared `DrillDownConfig` with `filter`, `maxRows` and `report` as `?: never` tombstones and `target` narrowed to 'drawer' | 'dialog'. The zod mirror's `drillDown` extends the shared mirror the same way, refusing each member by name, so `objectui validate` refuses a stored config carrying one. A row drills to the one record it already is; none of the four was ever read. The DrillDownConfig class docblock and the declared-keys pin comment no longer say every block honours every target arm. The 6576 anchor pins follow the new shape, the 7352 mirror pin's table leg runs over the values the table takes, and the 6576 changeset carries a dated note. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
✅ 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
|
`ObjectDataTableSchema.drillDown` now names `ObjectDataTableDrillDownConfig`, so the shared type is referenced only from docblocks, and `object-ui/no-unused-imports` fails `@object-ui/types#lint` on it. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
✅ 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
|
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
|
CI note (
Generated by Claude Code |
✅ 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: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL Generated by Claude Code |
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…ith a breaking banner The 10685 changeset narrows the published validator's accept set: `objectui validate` / `safeValidateSchema` refuse a stored `object-data-table` config carrying `drillDown.filter`, `.maxRows`, `.report` or `target: 'navigate'` that parsed green before. The repo's convention grades that `minor` with a breaking banner (as 6881, 6951, 6972, 7322, 7963, 8801 and 7352 do); `patch` was the grade for a TypeScript-door-only refusal, which no longer describes this change. `@object-ui/plugin-dashboard` stays `patch`: a React-prop narrowing with no runtime door. Every other sentence is kept. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
✅ 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: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #10685
Clause-②: no — a per-block narrowing, as the ruling recorded for objectui#9002
What this does
This applies objectui#9002's ruling B (comment
5643445104, a per-block refusal) to the sibling blocks ofobject-metric, as triage5837818071directed. Each block refuses, by name, theDrillDownConfigmembers it can never honour; the sharedDrillDownConfigkeeps every member for the blocks that read them. The seat's round-2 ruling (option A) widened the claim's file surface bypackages/types/src/objectql.tsandpackages/types/src/zod/objectql.zod.ts, so theobject-data-tablehalf shuts BOTH doors here, and merging this PR completes the card.@object-ui/typesadds two per-block shapes besideObjectMetricDrillDownConfigindata-display.ts, exported on the root entry and on the@object-ui/types/data-displaysubpath:ObjectPivotDrillDownConfig:DrillDownConfigplus amode?: nevertombstone (the fix(plugin-dashboard,types): object-metric refuses drillDown.filter and drillDown.mode on its prop type #10681 idiom: aREFUSED BY NAMEdocblock and@deprecated, namingobject-data-tableas the block that readsmode).ObjectDataTableDrillDownConfig:DrillDownConfigplusfilter?: never,maxRows?: neverandreport?: nevertombstones, andtarget?: 'drawer' | 'dialog'. Each tombstone names the blocks that do read the key.ObjectDataTableSchema.drillDowninobjectql.tsis typed withObjectDataTableDrillDownConfig. The data table's component prop is anchoredEqualto that schema by the two objectui#6576 pins, so the widget's prop refuses exactly what the schema refuses.object-data-tablemirror'sdrillDowninobjectql.zod.tsis the sharedDrillDownConfigSchemaextended so thatfilter,maxRowsandreportareretirementTombstonearms whose messages name the key and the blocks that read it, andtargetisz.enum(['drawer', 'dialog'])with an error naming the refused'navigate'.enabled,mode,titleandcolumnsparse exactly as before. Nothing else inobjectql.zod.tschanges. This is the doorobjectui validateandobjectui checkrun (safeValidateSchema), so a stored JSON table config carrying one of the four is now refused there.@object-ui/plugin-dashboard:ObjectPivotTableProps.schemagainsdrillDown?: ObjectPivotDrillDownConfig, and the component reads its drill config through that type instead of throughany.DrillDownConfig.modedocblock names onlyobject-data-table(none of charts, pivot tables or metric cards reads it) and says what each value does there; theDrillDownConfigclass docblock no longer says "targetis honoured by all of them", and the declared-keys pin's comment no longer names the table among thenavigateOnlydeliverers.object-chart: no type change (see H4). Its refused set is pinned.drill-down-per-block-10685.test.tsin@object-ui/types(onedescribeper block,@ts-expect-errorper refused member with the read members as controls, plus a RUNTIMEdescribeover the zod door), andObjectPivotTable.drillDownRefusal-10685.test.tsxin@object-ui/plugin-dashboard(the published component's prop). The two 6576 anchor pins are re-pointed at the table shape, and the 7352 mirror pin's table leg runs over the values the table now accepts, with a non-vacuity check that the host-synthesised table configs stay in it.10685-drilldown-per-block.md(@object-ui/typesminorwith a breaking banner,@object-ui/plugin-dashboardpatch) opens with the banner "Breaking for authored metadata, gradedminorby this repo's convention":objectui validate/safeValidateSchemanow refuse a storedobject-data-tableconfig carryingdrillDown.filter,.maxRows,.reportortarget: 'navigate'that parsed green before, none was ever read, and the migration is to delete the key or write'drawer'/'dialog'.minorbecause the change narrows the published validator's accept set and the publishedObjectDataTableSchematype, the grade every pending zod-door refusal in this repo carries (6881, 6951, 6972, 7322, 7963, 8801, 7352); objectui#9002'spatchwas graded on a TypeScript-door-only refusal, which does not describe this change.@object-ui/plugin-dashboardstayspatch: a React-prop narrowing with no runtime door, the 9002 shape. The changeset also saysobject-pivot's refusal is at the TypeScript door only (no zod mirror exists for it). H6:.changeset/7352-drill-down-config-mirror.mdand.changeset/6576-widget-schema-anchors.mdeach have the falsified sentence scoped "at this change" plus a⚠️ Dated note, 2026-09-25 … objectui#10685; frontmatter md5 before and after: 7352ecedb10c5189b2c8841ea736b67c3cc6both, 6576b25dbc26fe50c14d0b6f0cc8fe6cb9e1both.H1: the table, re-measured per block
Read sites were re-read on
origin/main5c61e524this round. The one-member-at-a-time runtime probe (rendered DOM, data source calls andopenRecordListcalls compared against a control, with and without aDrillNavigationProvider) was run in round 1 atbc97f9247and is recorded in report comment5839013332; it was not re-run, and no read site moved since.object-chartenabled(isDrillEnabled),filter(computeDrillFilter),title(resolveDrillTitle),targetincluding'navigate'(openRecordList),maxRows(drawerpageSize),columnsmode,report: already refused by the spec'sChartDrillDowntype and by the zod mirror (unrecognized_keys)object-pivotenabled,filter,title, andtarget,columns,maxRows,reportthroughDrillDownDrawermodeobject-data-tableenabled(isDrillEnabled),mode('record'opens the row;'filter'turns the drill off), a non-templatetitle,columns(the record drawer's field list),targetin two arms ('dialog', anything else drawn as a drawer)filter,maxRows,report;target: 'navigate'was drawn as a drawer@object-ui/core'sdrill-down.tshelpers readenabled,filterandtitleonly;DrillNavigationContextcarries one handler,openRecordList(objectName, filter); thedata-tablerenderer the table spreads its node into never namesdrillDown;RecordDetailDrawertakesfields,titleand a two-armedtarget.The card's runtime-door bullet also names
ChartSchema.drillDown. That is the plainchartnode, whose rendererChartRenderer.tsxreads nodrillDown; it is outside this card's three blocks and deliberately untouched, the plain-chartremainder.H2: can never honour, or not yet honoured (one line each)
object-pivot·mode: never. Every pivot click point is an aggregated bucket (a cell, a row or column header, a total), so the pivot always drills through;modechooses drill-to-record for a clicked ROW, and a pivot has none. This is the metric's reasoning in5643445104.object-chart·mode,report: never, by the contract. Its drill type is@objectstack/spec'sChartDrillDown, which declares neither; the spec's schema refuses both by name ("A chart segment is always an aggregate"). Already refused before this card; only pinned here.object-data-table·filter,maxRows,report: never. All three configure a drilled LIST (its scope, its row cap, the report that replaces it). The table drills to the RECORD its row already is,RecordDetailDrawerlists nothing, and the block'smode: 'filter'arm is pinned as "ignored", so there is no list drill for them to act on. A row that drills through to a list of another object would need a drill-target memberDrillDownConfigdoes not have: a new capability, not an unread member.object-data-table·target: 'navigate': refused, not honoured (H3).H3:
navigateon the data tableobject-charthonoursnavigateby callingopenRecordList(objectName, merged filter), the object's LIST page scoped by the drill filter.DrillNavigationContexthas no record-level handler and@object-ui/app-shell'suseOpenRecordListbuilds only the list route. The table's click point is one record, so the list page is the wrong destination, and a "navigate to this record" arm needs a new host seam, not a small hunk. So the table's shape refusesnavigateon both doors and an author gets a type error, or avalidaterefusal, instead of a drawer.H4: where the shape differs from PR objectui#10681
object-chartkeeps its spec-bound type.ObjectChartSchema.drillDownis already per-block (the spec'sChartDrillDown), pinnedEqualto that symbol byobject-chart-undeclared-keys-8885.test.ts. A tombstoned local type would unbind it from the spec. A freshmodeorreportliteral is already a compile error and the zod mirror already refuses both by name.object-pivotandobject-data-tableregistrations advertise nodrillDowninput (adding one widens the designer palette, a shape decision).object-chart's description is pinned bypackages/plugin-charts/src/index.test.tsto list exactly the six spec keys and NOTmodeorreport.safeValidateSchema, run byobjectui validate) reads that block's drill members and accepted all four; the round-1 probe showed the same door refuses a bad value onenabled, so it does reachdrillDown.object-pivothas no zod mirror, so its refusal is TypeScript-only and the changeset says so.Verification (measured at
430223947f=a1217b515merged withorigin/main5c61e524; head64d43b6d67= that head + a second merge oforigin/main41ae65b26+ the changeset commit)Round 3 delta and its gates (head
64d43b6d67). Against430223947fthe 14 files of this card differ in two places only: the changeset (theminorgrade and its banner) and one line ofindex.tsthatmain's own refactor(types,plugin-kanban): retire ColumnWidthConfig / ColumnWidthConfigSchema (objectui#10582) #10730 deleted (a retiredColumnWidthConfigexport); the other 12 are byte-identical, and the branch's diff againstmainis still exactly these 14 files. Re-run at this head, each exit 0:check-changeset-presence(11 source files of 2 released packages, 1 changeset);changeset:check;check-changeset-no-major(theChangeset Bump Policystep);check:changeset-claims(36 pending changesets, unchanged reading);check:pending-changeset-literals;check:new-line-citations(0 new citation(s));check:control-bytesOK;check-changeset-overwrite(report-only: it reports the 6576 and 7352 edits, the intended shape). No suite, type-check or ablation was re-run: the delta is a changeset plusmain's already-green commits merged with a cleanmerge-tree, and none of the measured sources moved.Closure build
pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-dashboard^...' run build, under the verify lock:VERDICT command-exit 0.dist/data-display.d.tsanddist/objectql.d.tscarryObjectDataTableDrillDownConfig(2 hits each),dist/index.d.tscarriesObjectPivotDrillDownConfig.pnpm --filter @object-ui/types run type-checkthenpnpm --filter @object-ui/plugin-dashboard run type-check:VERDICT command-exit 0.tsc --listFilesOnlyon eachtsconfig.test.jsonlists the four types pins and the two plugin-dashboard pins, and plugin-dashboard's program reads@object-ui/typesthroughdist/data-display.d.tsanddist/objectql.d.ts.pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-dashboard/from the repo root:Test Files 378 passed (378),Tests 6562 passed (6562),VERDICT command-exit 0.Gates, each exit 0:
check-changeset-presence(11 source files of 2 released packages, 1 changeset);check:new-line-citations(0 new citation(s));check:control-bytesOK;changeset:check(nomajor);check:pending-changeset-literals;check:changeset-claims(36 pending changesets name a touched file; the two whose prose is about drill-down, 7363 and 8885, were read and stay true, and the rest describe other regions of these files);check:spec-symbols;check:self-import;check:esm-specifiers;check:test-path-roots;check:vi-mock-specifiers;check:handler-key-reads;check:unreferenced-sources;check:metadata-write-doors;type-check:coverage;check:component-surface-parity;check:doc-types;check-governed-queue-guard --testover the 14 paths: NOT GOVERNED.NOT MEASURED:
check:readme-exportsexits 1 on a prerequisite (385 README self-imports unjudgeable because other packages'dist/index.d.tsare not built); this diff edits no README. Left to CI.ESLint was run on the 11 touched source and test files, not the whole repo. The narrowing is a measurement, for three reasons: the config is the root
eslint.config.jsand neither package has its own;--format jsonreports 11 files, 0 errors and 80 warnings (allno-explicit-any, none on a line this branch added, by intersecting message lines with the branch's added-line set);--print-configshows emptyparserOptions, so there is no type-aware linting and the diff cannot move any untouched file's verdict. Under--no-inline-configthe same run shows 1 error, the block-disabledSpecFormFieldalias inindex.tsthat is onmainand outside this diff; the packagelintscripts areeslint ., which honours the disable. Fullpnpm lintis left to CI.CI on this head:
Inert vi.mock Specifier Checkis red onmainsincef9c06ef6a(PR objectui#10729), filed as objectui#10732. Confirmed locally in a no-install probe worktree at5c61e524: exactly one hit,apps/console/src/__tests__/filterContextTokensSweep-10666.test.tsx, outside this surface; in an installed worktree the specifier is found throughnode_modulesand the checker passes. Not touched here.Red on base, then green on head
The five touched sources (
data-display.ts,index.ts,objectql.ts,zod/objectql.zod.ts,ObjectPivotTable.tsx) were swapped to theirorigin/main5c61e524blobs with the HEAD pins in place, then restored to HEAD (every blob equal toHEAD,git diff HEADempty, types dist rebuilt, markers back to 2 and 1).tsc -p tsconfig.test.json): exit 2.TS2305/TS2724on the two new type names imported by the per-block pin and the 6576 pin, and nineTS2578 Unused '@ts-expect-error'on the per-block pin's refusals (the pivot's two, the table's seven).Tests 4 failed | 59 passed (63), the four failures being the zod describe'sfilter,maxRows,reportandtargetrows.TS2305on the 6576 anchor'sObjectDataTableDrillDownConfigimport and twoTS2578on the pivot pin.Ablations (each through
ablation-replace.mjs: anchor hit once, blob changed, command run, restored to the HEAD blob with an emptygit diff HEAD)ObjectDataTableSchema.drillDownback to the sharedDrillDownConfig, with a JSDoc markerABLATED_10685_TS1on the line. Types program exit 2:TS2344on_SchemaTakesTheTableShape,TS2578onnodeWithFilterandnodeWithNavigate,TS2344on the 6576 pin'sassertionDrillDownDeclared, pluszod-mirror-parity.test.tsgoing red withobjectql.zod.ts#ObjectDataTableSchema(the repo's TS/zod parity pin sees the two doors disagree: a second, independent instrument). Dist leg: types rebuilt, marker count indist/objectql.d.ts1, plugin-dashboard program exit 2 (TS2344onObjectDataTable.schemaAnchor-6576'sEqual); after the restore rebuild the marker count is 0 andObjectDataTableDrillDownConfigis back to 2. A first pass of this leg read the dist marker with the wrong grep (tsc emits theimport()type with single quotes) and got 0; the downstream red was the same in both passes, and the marker form above is the evidence.nevertoDrillDownConfig['KEY']):filtergivesTS2578onwithFilterandnodeWithFilter;maxRowsonwithMaxRows;reportonwithReport;targetwidened back to the shared union givesTS2578onwithNavigateandnodeWithNavigate. Each also turnszod-mirror-parityred as above.ABLATED_10685_PIVOT): types programTS2578onwithModeandhandedAcross; dist marker 1; plugin-dashboard program exit 2 with twoTS2578on the pivot pin; marker 0 after the restore rebuild.ObjectChartSchema.drillDownto the shared type):TS2578on the chart pin'swithModeandwithReport, plus the 8885 pin'sTS2344andTS2578.Tests 4 failed | 59 passed (63), the four refusal rows. Each zod member alone (the tombstone renamed away, ortargetwidened to includenavigate): exactly one failure each, its own row,62 passed.Direction: red as predicted in every leg, with one increase (the parity pin) beyond the prediction.
Serial
origin/main5c61e524and PR objectui#8941's headbb7d5e718(claude/pr-7058-lucide-react-1.41.0) were fetched into private refs;git merge-tree --write-treeexits 0 against both. PR objectui#10707 and PR objectui#10643 have merged; objectui#10657 has no open PR and no branch (its test is onmain).mainhad moved in three of this PR's files (thefilterdocblocks inobjectql.ts, themaskedcolumn indata-display.ts, thefilterdescribes inobjectql.zod.ts), somainwas merged in (430223947f, a merge commit, no conflicts). In round 3mainhad moved again in one file (index.ts, #10730's retired export), soorigin/main41ae65b26was merged in the same way (4f2f7a530f, no conflicts). At head64d43b6d67,merge-tree --write-treeexits 0 againstorigin/main41ae65b26and against the three open PRs that touch these files: PR objectui#10734 (856ecc953,ObjectChartSchemaregions ofobjectql.ts/objectql.zod.ts, disjoint), PR objectui#10714 (218f642484,data-display.ts) and PR objectui#8941 (bb7d5e718,objectql.ts). The branch diff againstmainis exactly the 14 files of this card.Acceptance notes
mode: 'filter'onobject-data-tableturns the row drill off, asObjectDataTable.drill.test.tsxpins, instead of drilling through. The rewritten docblock says so. Value-level, outside the card's member set; recorded here only.maxRowsreaches each drill list as the drill table'spageSize(DrillDownDrawerand the chart's drawer), a page size rather than the "Hard cap on rows fetched" the shared docblock promises. Observation, outside this card.object-chart, non-fresh values. A value typed as the sharedDrillDownConfigis still assignable toObjectChartSchema.drillDown, because the spec type omitsmodeandreportrather than tombstoning them. No producer in the tree hands a typed shared config across.@objectstack/spec'sChartDrillDownSchemarefusal text formodecalls it "a TABLE / PIVOT / METRIC drill key"; onobject-pivotandobject-metricit is now refused. Reported to the seat in round 1 for the spec repository; not touched here.os-elon-muskin round 1 (read back matching); found empty at the start of this round and set again to match the card's assignee, per the dispatch rule.Session:
https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhNGenerated by Claude Code