You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
dashboards: a widget filterBindings key naming an undeclared dashboard filter is accepted at draft and publish (silently inert) #27445
Filing gate: ① a product defect found by a QA run (off-clause probe, RUNNER rule 8).
Reproduction
Boot the showcase and sign in as admin.
PUT /api/v1/meta/dashboard/qa_probe_badbind?mode=draft with body: {"name":"qa_probe_badbind","label":"x","widgets":[{"id":"aa","type":"metric","title":"b","dataset":"showcase_task_metrics","values":["task_count"],"filterBindings":{"no_such_filter":"status"}}]}
POST /api/v1/meta/dashboard/qa_probe_badbind/publish.
Expected: a refusal naming the unknown filter, as a binding to an unknown FIELD is refused (422, author-time rule dashboard-filter-field-unknown).
Actual: draft 200 and publish 200. The binding names no declared filter and does nothing at runtime.
Mechanism
packages/lint/src/validate-widget-bindings.ts checks only the effective bound field. No rule checks that each filterBindings key names a filter the dashboard declares. A typo in the filter name (for example regoin) therefore publishes clean, and the widget silently ignores the filter it was meant to opt out of or re-target.
QA-source: #27441 · dashboards.global-filters-rescope · observed beside the clauses
Read fresh. A widget filterBindings key naming an undeclared dashboard filter is accepted at draft and publish, and is silently inert. From the 18.3.0 RC run ([sweep] v18.3.0 release test sweep #27377 §5); it is retested on the next wave.
Refuse it at draft and publish with the declared filters named. That narrows the accept set: Clause-②: no (narrowing), the ADR-0087 disposition, and a contract review before approval.
Claim: PM loop round 3 · 2026-10-29T09:30Z
Session: session_01WGufzfhobWEgUUrjz4rrbh
Account: os-support-ai
Branch: claude/project-thread-m56rgp (card thread session_01WGufzeTeGTp7gp3ECrLPMf. The platform assigns the thread's branch and the thread can push only that one, so the branch is written as measured instead of claude/issue-27445-<slug>.)
Worktree: objectstack-issue-27445
Domain: domain:spec
Seat: domain:spec#1
File surface:
packages/lint/src/validate-widget-bindings.ts: a new error id, dashboard-filter-binding-unknown, inside validateWidgetBindings. It refuses a filterBindings key that names no declared filter, built from the dashboardFilterDefs set. The reserved dateRange key counts only when the dashboard declares dateRange. The message names the widget, the key, the declared filters, and a did-you-mean.
The rule-explanations entry, the lint suite member, and runtime-gate rows for draft and publish.
packages/spec/src/ui/dashboard.zod.ts: the filterBindings.describe() names the rule. There is no superRefine: a widget schema cannot see its dashboard's filters.
ADR-0087: one D3 entry. There is no D2, because the census found 0 producers.
PR #27527 merged through the queue at 03685047, which is an ancestor of origin/main. On 11 of the 12 paths the PR touched, the merge tree matches the accepted head 4759433d byte for byte. On rule-explanations.ts, the PR's net hunks from the merge-base 273137d2 are identical to 03685047^1..03685047. The bytes differ only because other work landed on main in between.
A widget filterBindings key that names no filter the dashboard declares is now refused with dashboard-filter-binding-unknown (error, not suppressible). The finding names the widget, the key, the declared filters and a did-you-mean. The refusal applies at os validate/build/lint and at the metadata publish door (422 INVALID_METADATA); a draft still saves and reports the verdict. A key is valid when it names a globalFilters entry, or is dateRange on a dashboard that declares dateRange. Stored rows still load. ADR-0087 D3 is ui-dashboard-filter-binding-unknown-refused. The review's upgrade-guide code-span note is #27532.
The card closed through Fixes #27445, and this seat removes pm:dispatched.
Activity
objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 经营报表 | none | P②
Triage: p2 ·
domain:spec·area:reports· 2026-10-29T08:09Z⛔ Not a claim.
filterBindingskey naming an undeclared dashboard filter is accepted at draft and publish, and is silently inert. From the 18.3.0 RC run ([sweep] v18.3.0 release test sweep #27377 §5); it is retested on the next wave.Clause-②: no (narrowing), the ADR-0087 disposition, and a contract review before approval.bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
Claim: PM loop round 3 · 2026-10-29T09:30Z
Session:
session_01WGufzfhobWEgUUrjz4rrbhAccount:
os-support-aiBranch:
claude/project-thread-m56rgp(card threadsession_01WGufzeTeGTp7gp3ECrLPMf. The platform assigns the thread's branch and the thread can push only that one, so the branch is written as measured instead ofclaude/issue-27445-<slug>.)Worktree:
objectstack-issue-27445Domain:
domain:specSeat:
domain:spec#1File surface:
packages/lint/src/validate-widget-bindings.ts: a new error id,dashboard-filter-binding-unknown, insidevalidateWidgetBindings. It refuses afilterBindingskey that names no declared filter, built from thedashboardFilterDefsset. The reserveddateRangekey counts only when the dashboard declaresdateRange. The message names the widget, the key, the declared filters, and a did-you-mean.rule-explanationsentry, the lint suite member, and runtime-gate rows for draft and publish.packages/spec/src/ui/dashboard.zod.ts: thefilterBindings.describe()names the rule. There is no superRefine: a widget schema cannot see its dashboard's filters.Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: highClause-②: no (narrowing)
Metadata-protocol: narrowed
Responsibility: n/a. This is a QA finding from QA run · release-18.3.0-w3-manual-browser (54/55) · c4c515ad · 2026-10-29 · 44 PASS / 2 PARTIAL / 8 FAIL / 0 BLOCKED / 1 NOT-RUN #27441 (RC run [sweep] v18.3.0 release test sweep #27377 §5).
Thread-read: 6491434802
Premise: held at
origin/main273137d2and hotcrm47a0666, measured by the dev in phase 1. The census found 0 offendingfilterBindingskeys in examples, the showcase, platformsystem_overviewand the four hotcrm dashboards. The reserveddateRangedef is built only when declared, in lint, in objectuiresolveDashboardFilterDefsand inpackages/verify.Serial constraints cleared: yes. No card in hand edits
validate-widget-bindings.tsordashboard.zod.ts. Seat 3's spec/plugin-detail: a field group's relatedList naming a side-card relationship saves, passes the spec and draws nothing at the group — the describe promises a body table there #27478 (plugin-detailrelatedList) and spec/Studio: a count rollup cannot be saved without summaryOperations.field, which the spec itself says is ignored for count #27462 (rollup) touch other files.Ruling-ref: triage
6491434802(p2, refuse, narrowing). Severity stays error, unlike chart-field-unknown errors on chartConfig binding keys the pinned renderer REFUSES — its message names a query failure that never happens #15463's ignored-key warning, because afilterBindingskey decides which rows the widget counts.objectstack-fleet commentedon Oct 29, 2026
Landed ·
domain:specseat 1 (#6017) · sessionsession_01WGufzfhobWEgUUrjz4rrbh· 2026-10-29T12:40ZPR #27527 merged through the queue at
03685047, which is an ancestor oforigin/main. On 11 of the 12 paths the PR touched, the merge tree matches the accepted head4759433dbyte for byte. Onrule-explanations.ts, the PR's net hunks from the merge-base273137d2are identical to03685047^1..03685047. The bytes differ only because other work landed onmainin between.A widget
filterBindingskey that names no filter the dashboard declares is now refused withdashboard-filter-binding-unknown(error, not suppressible). The finding names the widget, the key, the declared filters and a did-you-mean. The refusal applies atos validate/build/lintand at the metadata publish door (422 INVALID_METADATA); a draft still saves and reports the verdict. A key is valid when it names aglobalFiltersentry, or isdateRangeon a dashboard that declaresdateRange. Stored rows still load. ADR-0087 D3 isui-dashboard-filter-binding-unknown-refused. The review's upgrade-guide code-span note is #27532.The card closed through
Fixes #27445, and this seat removespm:dispatched.