Skip to content

Commit 1b2043e

Browse files
docs(lint): dashboard-filter-binding-unknown explanation counts a globalFilters entry named dateRange as declared (#29525)
<!-- claude-projects-attribution: {"github_login":"os-warren"} --> _Requested by **Warren** · [project thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8UFVez1aipH3zDDpgxd3umQ)_ Fixes #29382 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:`os explain dashboard-filter-binding-unknown` 的一句说明,和 `validate-widget-bindings.ts` 里两段注释;规则代码不变,新增一个测试。 - 为什么改:旧说明称「没声明 `dateRange` 的仪表盘上,`dateRange` 绑定一律报错」;实测规则接受名为 `dateRange` 的 `globalFilters` 条目,说明与行为不符。 - 风险与回滚:只改说明文字与注释,行为零变化;回滚即 revert 本 PR。 - 先看:`packages/lint/src/rule-explanations.ts`(第 407–410 行那句)、`packages/lint/src/validate-widget-bindings.test.ts`(新测试)。 - 两分钟验证:`pnpm --filter @objectstack/lint exec vitest run src/validate-widget-bindings.test.ts -t dashboard-filter-binding-unknown`,预期 13 个用例全绿。 - 需要你做的:无,席位落地。 ## What changes - `packages/lint/src/rule-explanations.ts`, rule `dashboard-filter-binding-unknown` only: the third paragraph said "A `dateRange` key on a dashboard that declares no `dateRange` is reported the same way". It now says a `dateRange` key is reported only when the dashboard declares neither `dateRange` nor a `globalFilters` entry named `dateRange`, because either one is a declared filter under that name. - `packages/lint/src/validate-widget-bindings.ts`: the rule family docblock and the `DASHBOARD_FILTER_BINDING_UNKNOWN` docblock say the same. No code line changed. - `packages/lint/src/validate-widget-bindings.test.ts`: one new case in the `dashboard-filter-binding-unknown` block. The dashboard has no `dateRange`, a `globalFilters` entry `{ name: 'dateRange', field: 'signed_at', type: 'date' }`, and a widget with `filterBindings: { dateRange: false }`. It expects zero findings. - `.changeset/29382-filter-binding-daterange-prose.md`: a patch for `@objectstack/lint`, because the explanation text ships in its `dist/`. ## Why The card asked for a measurement first. The rule takes its declared names from `dashboardFilterDefs`, which adds every `globalFilters` entry under its `name`. So a `globalFilters` entry named `dateRange` is declared even when the dashboard has no `dateRange`. The new test measured it on `origin/main` 94d3e25 before any prose edit: the case passed with zero findings. The rule accepts the binding, so this PR takes the prose-only arm and leaves the rule body alone. The runtime agrees: objectui `resolveDashboardFilterDefs` (`ui/packages/core/src/utils/dashboard-filters.ts`) builds the same name set. ## Tests - Measurement, before any prose edit, tree 94d3e25 plus the new test only: `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-widget-bindings.test.ts -t 'dashboard-filter-binding-unknown'`: `Tests 13 passed | 178 skipped (191)`, lock VERDICT command-exit 0. - Final head: see Verification. ## Acceptance notes - `packages/spec/src/ui/dashboard.zod.ts` still says `dateRange` is "reserved for the built-in dashboard date range" (the `globalFilters[].name` TSDoc) and "(dateRange only when declared)" (the `filterBindings` describe). Both are on the spec side that open PR #29370 owns, which keeps a `globalFilters` entry named `dateRange` accepted on a dashboard without `dateRange`. They are outside this card's file surface. carrier: PR #29370. - The finding's hint for an undeclared `dateRange` key still reads "Declare `dateRange` on the dashboard, or delete the `dateRange` entry." It is reached only when neither declaration exists, so it is accurate; it does not mention the `globalFilters` spelling. Not changed, because the claim limits the rule body to the false-positive arm. ## Verification All readings are on head `abff9a05` (`b6d1ad0e` plus a merge of `origin/main` 86d36e9). <details><summary>Package tests and typecheck</summary> - `pnpm --filter @objectstack/lint test`: `Test Files 190 passed (190)`, `Tests 7858 passed (7858)`, lock VERDICT command-exit 0. - `pnpm --filter @objectstack/lint typecheck`: lock VERDICT command-exit 0. </details> <details><summary>Derived gates (dispatch-gates --commands, 58 families)</summary> - 57 of 58 exit 0, each captured before any pipe. - `pnpm check:dual-build-cjs-loads`: exit 3, PREREQUISITE NOT MET (it needs every package's `dist/`). NOT MEASURED locally; CI's `Lint & Repo Gates` runs it on a full build. - `dispatch-gates --ran`: "58 derived famil(ies) accounted for, 57 run, 1 NOT-MEASURED". - `node scripts/check-nul-bytes.mjs`: exit 0. </details> Repo-wide `pnpm lint` and every other CI family: read from this PR's check-runs. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (no spec change) - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes. (repo-wide lint is CI's run) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. - [x] 6. `packages/spec` touched: not touched. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English (the maintainer brief is Chinese by the repo's communication rule). Branch `claude/project-thread-xi96xf` is the card thread's platform-assigned branch, as the Claim records. Written by `domain:spec#5 · os-warren · session_01JQwxbaAMsVe1WU74CypjTZ`. --- _Generated by [Claude Code](https://claude.ai/code/session_01JQwxbaAMsVe1WU74CypjTZ)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7f368f5 commit 1b2043e

4 files changed

Lines changed: 29 additions & 6 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/lint': patch
3+
---
4+
5+
docs(lint): the `dashboard-filter-binding-unknown` explanation no longer says a `dateRange` binding is always reported on a dashboard without `dateRange`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`os explain dashboard-filter-binding-unknown` said a widget's `filterBindings.dateRange` key "on a dashboard that declares no `dateRange` is reported the same way". The rule never did that when the dashboard declares a `globalFilters` entry named `dateRange`: every `globalFilters` entry's name is a declared filter, so the key binds it and the rule accepts it, as the explanation's first paragraph already says. The sentence now reads that a `dateRange` key is reported only when the dashboard declares neither `dateRange` nor a `globalFilters` entry named `dateRange`. The rule's behaviour is unchanged; a new test pins the accepted case.

‎packages/lint/src/rule-explanations.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,9 @@ const DASHBOARD_FILTER_BINDING_UNKNOWN_EXPLANATION: RuleExplanation = {
405405
'way the widget counts different rows than the author wrote, with nothing on the page to say so, ' +
406406
'which is why the rule is an error and is not suppressible.',
407407
'To fix it, rename the key to a declared filter name (the finding names the nearest one), declare ' +
408-
'the filter on the dashboard, or delete the entry. A `dateRange` key on a dashboard that declares ' +
409-
'no `dateRange` is reported the same way: there is no date range for it to bind.',
408+
'the filter on the dashboard, or delete the entry. A `dateRange` key is reported the same way ' +
409+
'only when the dashboard declares neither `dateRange` nor a `globalFilters` entry named `dateRange`: ' +
410+
'either one is a declared filter under that name, and with neither there is nothing for it to bind.',
410411
],
411412
};
412413

‎packages/lint/src/validate-widget-bindings.test.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,14 @@ describe('dashboard-filter-binding-unknown (#27445)', () => {
948948
expect(findings[0].hint).toBe('Declare `dateRange` on the dashboard, or delete the `dateRange` entry.');
949949
});
950950

951+
it('a `globalFilters` entry named `dateRange` declares the name on a dashboard without `dateRange`', () => {
952+
const findings = only(validateWidgetBindings(stack(
953+
{ dateRange: undefined, globalFilters: [{ name: 'dateRange', field: 'signed_at', type: 'date' }] },
954+
{ filterBindings: { dateRange: false } },
955+
)));
956+
expect(findings).toEqual([]);
957+
});
958+
951959
it('a dashboard with no filters at all refuses every key and says so', () => {
952960
const findings = only(validateWidgetBindings(stack(
953961
{ dateRange: undefined, globalFilters: undefined },

‎packages/lint/src/validate-widget-bindings.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,12 @@ import {
7979
* counts, which is why this is an error and not the warning tier
8080
* `chart-field-unknown` took for its ignored keys (#15463): those re-dress
8181
* the same numbers, this one changes them. The declared names are the ones
82-
* {@link dashboardFilterDefs} builds, so the reserved `dateRange` key is
83-
* valid only on a dashboard that declares `dateRange` — the runtime, this
84-
* file and `@objectstack/verify`'s window proof all build that def only then.
82+
* {@link dashboardFilterDefs} builds: every `globalFilters` entry's name,
83+
* and the reserved `dateRange` when the dashboard declares `dateRange` (the
84+
* runtime, this file and `@objectstack/verify`'s window proof all build that
85+
* built-in def only then). A `globalFilters` entry named `dateRange` is a
86+
* declared filter by that name too, so a `dateRange` key binds it on a
87+
* dashboard that declares no `dateRange`.
8588
*
8689
* Advisory rules — severity `warning`, build stays green:
8790
*
@@ -450,7 +453,8 @@ export const DASHBOARD_FILTER_FIELD_UNPROVISIONED = 'dashboard-filter-field-unpr
450453
export const DASHBOARD_FILTER_FIELD_NOT_INCLUDED = 'dashboard-filter-field-not-included';
451454
/**
452455
* [#27445] A widget `filterBindings` key that names no filter the dashboard
453-
* declares (`dateRange` counts only when the dashboard declares one).
456+
* declares (`dateRange` counts when the dashboard declares `dateRange` or a
457+
* `globalFilters` entry named `dateRange`).
454458
*/
455459
export const DASHBOARD_FILTER_BINDING_UNKNOWN = 'dashboard-filter-binding-unknown';
456460
/** [#14148] A key of the widget's OWN `filter` that resolves to no column. */

0 commit comments

Comments
 (0)