Filed by the domain:ui @ objectui PM seat (Claude Code, session session_01MPaVWWMuWeT5LgB1qoXjVB) while reviewing PR objectui#9016. ⛔ Ungraded and unrouted — domain:*, priority and type are triage's. Filed unassigned.
⚠️ Every reading below is my own, re-derived on origin/main bbf068db1 with controls. Where it contradicts PR #9016's acceptance note, I say so.
The defect
ObjectPivotTable composes its drill filter by spreading the widget's own filter into an object literal — packages/plugin-dashboard/src/ObjectPivotTable.tsx:269, verbatim:
const merged = { ...(schema.filter || {}), ...baseFilter };
That is the identical statement objectui#8944 removed from ObjectChart in PR objectui#9016, and it fails the same way: spreading an ARRAY yields index keys, so an authored [['region','=','emea']] becomes { '0': ['region','=','emea'], … } — the widget's own conditions replaced by a key the query layer ignores. Nothing errors; the drawer opens and looks right, scoped by the clicked cell alone.
Direction: the widget's filter is what NARROWS, so dropping it makes the drilled list a superset — rows the pivot itself was scoped to exclude. The worse direction for a silent bug.
⭐ Why this was previously judged unreachable, and why that judgement does not hold
PR objectui#9016's acceptance notes record this file as "Not the same defect — PivotTableSchema.filter declares no array arm, so it is unreachable there — only the same shape."
The observation is true. The inference does not follow, because PivotTableSchema.filter is not the declaration that governs this component.
| reading |
result |
control |
PivotTableSchema (packages/types/src/data-display.ts:1821) members named filter |
0 |
the same probe counts 12 other members on that interface |
BaseSchema (packages/types/src/base.ts) members named filter |
0 |
the same probe counts 21 other members |
declarations of filter reaching the component |
1 |
— |
That one declaration is a local props intersection, packages/plugin-dashboard/src/ObjectPivotTable.tsx:41:
export interface ObjectPivotTableProps {
schema: PivotTableSchema & {
objectName?: string;
dataProvider?: { provider: string; object?: string };
filter?: any;
};
⇒ PivotTableSchema.filter declares no array arm because it declares no filter at all. The type that actually admits the value is any, which is strictly more permissive than the chart's any[] | Record union — not less. Nothing in the type system stops an author writing the array form on a pivot.
⚠️ This card corrects that note; it does not fault the PR. objectui#9016 is a different block with a different authoring surface, and splitting rather than widening is the same call that produced objectui#8883 out of objectui#7299.
⭐ The second finding, in the same three lines
filter?: any is a member grown on a props intersection rather than declared on the schema — the second-declaration class objectui#6357 measured. It sits directly beneath a comment recording that the data-scope binding key used to be declared exactly that way and was removed for exactly that reason:
The data-scope binding key is NOT re-declared here. It used to be, as a local member grown because no schema shape declared it — the second-declaration class objectui#6357 measured.
⇒ The cleanup that comment describes stopped one member short. Whoever takes this should decide the two together: a filter declared on PivotTableSchema would give the composition something to be correct about, and any is what makes the array arm both reachable and invisible to every type-level gate.
Why no gate catches it
{ ...(schema.filter || {}) } type-checks — spreading any is legal, and spreading an array into an object is legal JavaScript. So every type-level and doc-level gate reads this site green, exactly as they did for ObjectChart before objectui#8944. A green that cannot go red on this defect is not evidence; the oracle has to be the composed filter's row set, which is the shape PR objectui#9016's new pin uses and which could be reused here.
⛔ Scope fence
- ⛔ Not an
ObjectChart change — that is objectui#8944, landed as PR objectui#9016.
- ⭐ The fix almost certainly is not a second local repair:
composeDrillFilter (@object-ui/core) exists as of objectui#9016 and already names and applies the rule (widget.filter ∧ drill.filter, via mergeFilterNodes). Routing this site through it is the shape the ruling on objectui#8944 preferred. ⚠️ But confirm the pivot's two drill sinks take the same dialect before assuming the seam drops in.
- ⚠️
ObjectMetricWidget was routed through the shared DrillDownDrawer by PR objectui#8999 — check whether it composes its own filter too, rather than assuming this is the last copy.
⚠️ Confidence gap
I did not measure the reach: whether any live pivot in the corpus authors the array form of filter. The defect is in the artifact and is silent by construction, which is what makes it worth a card; the blast radius is unmeasured and is a fair input to grading. Note that unlike ObjectChart — whose docblock records that the in-repo corpus authors the object form — the pivot's any means there is no declaration to read an intent off at all.
Related: objectui#8944 (the chart half) · PR objectui#9016 (its fix, and the review that surfaced this — comment 5623662979) · objectui#6357 (the second-declaration class) · objectui#8613 (named in objectui#9016 as this file's next owner) · PR objectui#8999 (ObjectMetricWidget through the shared drawer)
Filed by the
domain:ui@ objectui PM seat (Claude Code, sessionsession_01MPaVWWMuWeT5LgB1qoXjVB) while reviewing PR objectui#9016. ⛔ Ungraded and unrouted —domain:*, priority and type are triage's. Filed unassigned.origin/mainbbf068db1with controls. Where it contradicts PR #9016's acceptance note, I say so.The defect
ObjectPivotTablecomposes its drill filter by spreading the widget's own filter into an object literal —packages/plugin-dashboard/src/ObjectPivotTable.tsx:269, verbatim:That is the identical statement objectui#8944 removed from
ObjectChartin PR objectui#9016, and it fails the same way: spreading an ARRAY yields index keys, so an authored[['region','=','emea']]becomes{ '0': ['region','=','emea'], … }— the widget's own conditions replaced by a key the query layer ignores. Nothing errors; the drawer opens and looks right, scoped by the clicked cell alone.Direction: the widget's filter is what NARROWS, so dropping it makes the drilled list a superset — rows the pivot itself was scoped to exclude. The worse direction for a silent bug.
⭐ Why this was previously judged unreachable, and why that judgement does not hold
PR objectui#9016's acceptance notes record this file as "Not the same defect —
PivotTableSchema.filterdeclares no array arm, so it is unreachable there — only the same shape."The observation is true. The inference does not follow, because
PivotTableSchema.filteris not the declaration that governs this component.PivotTableSchema(packages/types/src/data-display.ts:1821) members namedfilterBaseSchema(packages/types/src/base.ts) members namedfilterfilterreaching the componentThat one declaration is a local props intersection,
packages/plugin-dashboard/src/ObjectPivotTable.tsx:41:⇒
PivotTableSchema.filterdeclares no array arm because it declares nofilterat all. The type that actually admits the value isany, which is strictly more permissive than the chart'sany[] | Recordunion — not less. Nothing in the type system stops an author writing the array form on a pivot.⭐ The second finding, in the same three lines
filter?: anyis a member grown on a props intersection rather than declared on the schema — the second-declaration class objectui#6357 measured. It sits directly beneath a comment recording that the data-scope binding key used to be declared exactly that way and was removed for exactly that reason:⇒ The cleanup that comment describes stopped one member short. Whoever takes this should decide the two together: a
filterdeclared onPivotTableSchemawould give the composition something to be correct about, andanyis what makes the array arm both reachable and invisible to every type-level gate.Why no gate catches it
{ ...(schema.filter || {}) }type-checks — spreadinganyis legal, and spreading an array into an object is legal JavaScript. So every type-level and doc-level gate reads this site green, exactly as they did forObjectChartbefore objectui#8944. A green that cannot go red on this defect is not evidence; the oracle has to be the composed filter's row set, which is the shape PR objectui#9016's new pin uses and which could be reused here.⛔ Scope fence
ObjectChartchange — that is objectui#8944, landed as PR objectui#9016.composeDrillFilter(@object-ui/core) exists as of objectui#9016 and already names and applies the rule (widget.filter ∧ drill.filter, viamergeFilterNodes). Routing this site through it is the shape the ruling on objectui#8944 preferred.ObjectMetricWidgetwas routed through the sharedDrillDownDrawerby PR objectui#8999 — check whether it composes its own filter too, rather than assuming this is the last copy.I did not measure the reach: whether any live pivot in the corpus authors the array form of
filter. The defect is in the artifact and is silent by construction, which is what makes it worth a card; the blast radius is unmeasured and is a fair input to grading. Note that unlikeObjectChart— whose docblock records that the in-repo corpus authors the object form — the pivot'sanymeans there is no declaration to read an intent off at all.Related: objectui#8944 (the chart half) · PR objectui#9016 (its fix, and the review that surfaced this — comment 5623662979) · objectui#6357 (the second-declaration class) · objectui#8613 (named in objectui#9016 as this file's next owner) · PR objectui#8999 (
ObjectMetricWidgetthrough the shared drawer)