fix(lint): chart-field-unknown warns on the chartConfig binding keys the pinned renderer refuses (#15463) - #15571
fix(lint): chart-field-unknown warns on the chartConfig binding keys the pinned renderer refuses (#15463)#15571claude[bot] wants to merge 2 commits into
Conversation
…the pinned renderer refuses (#15463) The rule id covers exactly three positions — `chartConfig.xAxis.field`, `chartConfig.yAxis[].field` and `chartConfig.series[].name` — and the `@object-ui` revision this repo pins (`.objectui-sha`) refuses all three as bindings, so the data failure the messages named ("the query result will not contain it") never happens. `axisPresentation` builds an axis's presentation minus its `field`, structurally rather than by a guard, so the x-axis stays `buildChartSeries`' `xAxisKey` (the widget's `dimensions[0]`) and a y-axis entry keeps only its slot, scale and chrome. `mergeAuthoredSeries` matches an authored entry BY NAME against one derived series per entry of `values`, and an entry matching none is ignored whole — the mark, colour, stack and axis side hung on it land on nothing. That is an ignored key, which is `widget-legacy-analytics-shape`'s class in this same file, reported there at `warning` ("the dashboard renderer ignores them ... a silent no-op"). All three positions drop to `warning`, suppressible per widget, and each message states its own consequence: the axis positions and the series position are refused for different reasons and had been sharing one sentence. The finding is KEPT — unlike the #14436 over-reach this measurement came from, the metadata really is wrong. The tier drop is a behaviour change on the `sys_metadata` publish door. The 2026-08-15 ruling put the rule's whole error set there as one "this board cannot render" class; that set was six ids and is now five. The demoted id still RUNS at the door and still reaches the author, on the advisory channel — pinned as its own case in runtime-gate.test.ts so "demoted" cannot decay into "dropped". `packages/platform-objects` is untouched: the shipped `system_overview` dashboard declares no `chartConfig` at all and stays at zero findings. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…art-field-unknown-tier
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 5 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e775f0a5d4fb8e5f7308ead1ea4a0b129ff50639 && git checkout e775f0a5d4fb8e5f7308ead1ea4a0b129ff50639
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 66086d06ad8d688c7c1a505dbe008acc53d64637 101eeed5475d95eb6d71fece83b87601bd38bf3b && git checkout -B drift-repro 66086d06ad8d688c7c1a505dbe008acc53d64637 && git merge --no-ff 101eeed5475d95eb6d71fece83b87601bd38bf3b
node scripts/docs-audit/affected-docs.mjs --json 66086d06ad8d688c7c1a505dbe008acc53d64637
|
|
ACCEPT — PM seat Reviewed against the three-dot diff What I checked in the diff:
Dev's verification quoted: Out-of-scope finding filed by the dev as #15575 ( Flipping ready + enabling auto-merge. Fixes #15463. Serial queue on Generated by Claude Code |
|
Standing-down note — PM seat This PR has been ARMED but red since 22:01:36Z: Spending the ONE re-run the dispatch rules allow: Generated by Claude Code |
Fixes #15463
Ruling from the grade (comment 5543882362), restated in the claim: option (2) with the finding kept — the three
chartConfigbinding keys the pinned renderer refuses drop fromerrortowarning, and the message stops describing a query that never runs.Which positions the rule id covers, and what changed per position
chart-field-unknownis pushed from exactly two call sites inpackages/lint/src/validate-widget-bindings.ts— thexAxislimb, andmeasureField, which serves the other two. That is the whole of the id's surface:chartConfig.xAxis.fielderrorwarning, suppressibleaxisPresentationbuilds the axis presentation minus itsfield. The x-axis key isbuildChartSeries'xAxisKey, i.e. the widget'sdimensions[0]; the authored key re-points nothing.chartConfig.yAxis[].fielderrorwarning, suppressiblechartConfig.series[].nameerrorwarning, suppressiblemergeAuthoredSeriespairs an authored entry with the derived series whosedataKeyit equals, one per entry ofvalues. An entry matching none is ignored whole, so the mark, colour, stack and axis side hung on it land on nothing.There is no fourth position, and no position of this id that a query does read — so nothing keeps the old tier. The three sibling ids that judge chart bindings on surfaces a query does read (
chart-dimension-unknown/chart-measure-unknowninvalidate-chart-bindings.ts,react-chart-field-unknowninvalidate-react-page-props.ts) are different rule ids and are untouched.Why the tier moved
Read at the
@object-uirevision this repo pins (.objectui-sha=00d3f09c500c4a45b5f27aca8af80349412faaf1),@object-ui/coresrc/utils/chart-presentation.tsstates the data/presentation split outright: series membership and the column each binding reads — "buildChartSeries'dataKeys,xAxisKey, and the spec's two binding keysChartSeries.nameandChartAxis.field" — are derived and never forwarded.axisPresentationdropsfieldstructurally rather than by a guard;mergeAuthoredSeriessays an entry naming a measure outside the selection is "ignored — membership belongs to the dataset, so an author cannot add, remove or re-point a series from the chart config". The renderer pins it by name inDatasetWidget.chartConfig.test.tsx(:179"ignores an authored axisfieldand keeps the derived axis binding",:192"ignores an authored series and keeps one derived series per measure").So "the query result will not contain it" named a failure that cannot occur, and
errorblocked a build and a Studio publish over a key that changes nothing at runtime. That is the classwidget-legacy-analytics-shapereports atwarningin the same file ("the dashboard renderer ignores them … a silent no-op"), and this id now carries the same tier, the same suppressibility and the same kind of sentence — one per position, because the axis positions and the series position are refused for different reasons and had been sharing one.The finding is kept: unlike the #14436 over-reach this measurement came from, the metadata really is wrong — the author wrote a binding and believes it is in force.
The publish door
The 2026-08-15 ruling put
validateWidgetBindings' whole error set on thesys_metadatapublish door as one "this board cannot render" class. That class was six ids and is now five; a dashboard write whose only reference-integrity problem is a refusedchartConfigbinding key is no longer a 422 and publishes with the finding on the advisory channel.runtime-gate.test.ts's accept-set test goes through that line rather than around it (renamed to ALL FIVE,chart-field-unknownasserted absent), and the demoted id gets its own case immediately after, asserting it still runs at the door and still reaches the author — so "demoted" cannot decay into "dropped".packages/platform-objectsis untouched (ADR-0072 D1).Verification
Every exit code captured before any pipe (
cmd > log 2>&1; EXIT=$?). All numbers below are from the final commit,101eeed54.pnpm --filter @objectstack/lint test→Test Files 95 passed (95)/Tests 2960 passed (2960); lock line:os-verify-lock: VERDICT command-exit 0 · held the lock 68s (1m08s) · waited 171s (2m51s)pnpm --filter @objectstack/lint typecheck→ exit 0;check:test-typecheck: OK — @objectstack/lint's test layer compiles under packages/lint/tsconfig.test.json(so the two edited test files are measured, not excluded)Targeted re-run of the two changed test files →
Test Files 2 passed (2)/Tests 182 passed (182)Fixture pass over shipped metadata, with an input-side control (throwaway harness, not committed):
validateWidgetBindingsoverSystemOverviewDashboard+SystemOverviewDatasetsfrompackages/platform-objects/src/apps/dashboards/→SHIPPED total findings: [],SHIPPED chart-field-unknown count: 0. The zero is non-vacuous: the same pass on the same fixture with one synthetic widget added (chartConfig: { xAxis: { field: 'no_such_dimension_xyz' } }) yields exactly1finding,severity: "warning", messagechartConfig.xAxis.field "no_such_dimension_xyz" does not resolve to a dimension of dataset "sys_audit_log_metrics" … the x-axis stays bound to this widget's first dimension (user_id). The binding is a silent no-op, not a query that fails.The shipped board declares nochartConfigat all, which is why its zero is structural.pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.dashboard-dataset-publish-gate.test.ts src/reference-sites.derivation.test.ts(the publish-door consumer, its dependency closure built first) →Test Files 2 passed (2)/Tests 27 passed (27)pnpm -s check:pm-dispatch-gates→✓ dispatch-gates self-test: 1415 cases pass.(no case edited)pnpm check:nul-bytes→ exit 0; plus a direct control-byte scan of the five changed paths (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]') → no matchespnpm lint→ exit 0. The full repo scan (eslint . --no-inline-config), not a narrowed one.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack→ 47 commands, all run. 45 exit 0. The two exceptions are NOT MEASURED, not failures, and both say so themselves:pnpm check:dual-build-cjs-loads→ exit 3,PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/. … ⛔ This is NOT a pass: nothing was measured.pnpm check:type-check-debt→ exit 3,check-type-check-coverage: PREREQUISITE NOT MET … ⛔ This is NOT a pass and NOT a finding: nothing was measured.Both need a whole-repo
turbo run build, which CI does before those steps. Neither reads a file this PR changes.The first derivation ran on a tree five commits behind
origin/mainand said so, namingscripts/pm/dispatch-gates.mjsitself among the stale files.origin/mainwas merged in (101eeed54) and the derivation re-run on the fresh tree: same 47 commands, byte-identical, and the whole gate union above was run after that merge commit.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code