Repository navigation
fix(lint): a widget filter keyed on a dataset dimension publishes, and a measure filter key is refused with its own message (#30006) - #30560
Conversation
…a measure key in both filter rules judgeDatasetFilterKeys now holds the one accept set for a bare filter key that widget-filter-field-unknown and report-filter-field-unknown share: a field of the object, then a dimension of the dataset. A bare key naming a dataset measure gets its own verdict and message: the dataset door compiles it to a row-level predicate on the measure's column (or to no column at all), never to a filter on the aggregated value. Claude-Session: https://claude.ai/code/session_01Utp6CbPHd9hdK2yxiVhr84 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CbPHd9hdK2yxiVhr84 Co-authored-by: Claude <noreply@anthropic.com>
…-xi54xm Claude-Session: https://claude.ai/code/session_01Utp6CbPHd9hdK2yxiVhr84 Co-authored-by: Claude <noreply@anthropic.com>
…imension accept widens Claude-Session: https://claude.ai/code/session_01Utp6CbPHd9hdK2yxiVhr84 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 2 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 4 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 41596954acc205e4a91f3e9bb5c4cfeef1bd8bcc && git checkout 41596954acc205e4a91f3e9bb5c4cfeef1bd8bcc
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 48b3ae6eb7cd234dcdea467fb55dd493a56bb0c2 b498e10ffbca375b172585ab9e15188e0f17bc9e && git checkout -B drift-repro 48b3ae6eb7cd234dcdea467fb55dd493a56bb0c2 && git merge --no-ff b498e10ffbca375b172585ab9e15188e0f17bc9e
node scripts/docs-audit/affected-docs.mjs --json 48b3ae6eb7cd234dcdea467fb55dd493a56bb0c2
|
…mensions a filter key may also name Claude-Session: https://claude.ai/code/session_01Utp6CbPHd9hdK2yxiVhr84 Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flagsNone. A dotted Implemented-by: VERDICT: PASS |
|
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC 维护者速读(席位终稿)
席位意见:同意落地。两条规则从此用同一套键,度量键的取舍有运行时实测撑着。 席位核对ACCEPT · head
|
domain:server#3 · os-bill · session_01Utp6CbPHd9hdK2yxiVhr84
Requested by Bill · project thread
Fixes #30006
Clause-②: yes (widening)
Metadata-protocol: none
Skills-docs: updated here — content/docs/deployment/validating-metadata.mdx
维护者速读
filter现在可以直接写数据集维度名(如维度state对应字段status),发布时不再误报"字段不存在";部件和报表两条规则改为共用同一套可接受键,写度量名的过滤键则给出专门的拒收提示。@objectstack/lint两条规则、说明文字和validating-metadata.mdx一句话;回滚即撤销本 PR。packages/lint/src/validate-widget-bindings.ts(judgeDatasetFilterKeys的维度/度量分支)、packages/lint/src/validate-report-runtime-filter.ts、packages/metadata-protocol/src/protocol.dashboard-dataset-publish-gate.test.ts的 lint: the dataset filter-key rules refuse a bare dimension or measure name that the dataset door binds at run time #30006 块。pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.dashboard-dataset-publish-gate.test.ts,预期 19 个测试全绿,其中 lint: the dataset filter-key rules refuse a bare dimension or measure name that the dataset door binds at run time #30006 块 4 个。What changes
widget-filter-field-unknownandreport-filter-field-unknownnow judge a bare filter key against one accept set, held injudgeDatasetFilterKeys(packages/lint/src/validate-widget-bindings.ts), so the two rules cannot drift:dataset-filter-field-binding.ts);kind: 'measure') and its own message in both rules, instead of the generic unknown-field verdict with a "Did you mean". The fix line names the measure's column when it aggregates exactly one (key the filter by the field "amount"), and points at fields and dimensions otherwise. A measure sharing a field's name is still the field, and accepted.The
os explainentries for both rule ids state the accept set and why a measure is refused. Rule ids, severities and every previously accepted key are unchanged.content/docs/deployment/validating-metadata.mdx: the package-closure paragraph (added by #30231) said "a filter key the object lacks is refused there"; it now reads "a filter key that neither the object nor the dataset's dimensions name is refused there", which is what both rules answer after this change.Which outcome for measures, and the evidence
Refusal with its own message. The card asked to measure whether a measure filter is a supported run-time shape. Measured on
mainatfa9c0aa2with a throwaway probe throughAnalyticsService.queryDataset(datasetorders_statesoverorders: dimensionstate→ fieldstatus; measuresamount_sum= sum ofamount,order_count= count,avg_amt= derived ratio), both strategies:runtimeFilter{ state: 'open' }(dimension)WHERE status = $1{ status: 'open' }{ amount_sum: { $gt: 100 } }(sum measure)WHERE amount > $1, a row-level filter on the raw column, not on the sum{ amount: { $gt: 100 } }{ order_count: { $gt: 5 } }(count measure)WHERE * > $1, broken SQL*{ avg_amt: { $gt: 1 } }(derived measure)400 INVALID_FIELD, listing "Valid filter members: state"400 INVALID_FIELD{ nope: 1 }(CONTROL)400 INVALID_FIELD400 INVALID_FIELDSo the door binds a dimension name the way the author means it, and binds a measure name to something other than the measure: the raw column for a sum or avg, no column for a count, nothing at all for a derived or duration measure. The door's own refusal lists only dimensions as valid filter members. Accepting measure keys would publish filters that silently filter rows when the author meant to filter on the total. A measure key was already refused by both rules (as an unknown field), so the new message refuses nothing that published before: no narrowing.
Tests
packages/lint/src/validate-widget-bindings.test.ts, new blockwidget-filter-field-unknown — dataset dimensions and measures (#30006): a dimension key is accepted (bare and under$or/$not); a sum measure key is refused at its path with the measure message and its column in the fix; a count and a derived measure are refused with the no-single-column message; a measure sharing a field's name is accepted; CONTROL: an unknown bare key and a dotted key are still refused as unknown. The existing hint pin now reads "a field …, a dimension of that dataset, or a relationship path …".packages/lint/src/validate-report-runtime-filter.test.ts: a reportruntimeFilteron a measure name is refused with the measure message; the twin-parity test now includes the dimension keystateand the measure keyhours_sum, and both rules answer them alike.packages/metadata-protocol/src/protocol.dashboard-dataset-publish-gate.test.ts, new block#30006, through the real save and publish path (draft PUT, then publish): a widget filter on a dimension name publishes; a reportruntimeFilteron a measure name is refused422 INVALID_METADATAatreports[0].runtimeFilter.amount_sumwith the measure message, and nothing active is stored; the widget twin refuses the same measure key; CONTROL: an unknown key beside the dimension key is still refused as unknown, as the only issue.Test runs on the merged head (after merging main, which brought #30231); the docs-only commit b498e10 changes no test input
Ablation (one, on the dimension accept)
Committed head
a8b06ca5.scripts/ablation-replace.mjsreplacedif (bare && dimensions.has(field)) return;with a guard that never fires, carrying the markerABLATION_30006;@objectstack/lintwas rebuilt andablation-dist-preflight.mjsfound the marker in 4 built files, because the door test reads@objectstack/lintfromdist/. Result: 7 tests red, every one a dimension-key assertion. In lint: the new widget dimension test, the report rule's dimension test and both of itsvalidate/buildgate tests. At the door: the #29900 dimension CONTROL, the new widget dimension test, and the new CONTROL board, which carries the dimension key. Restore: blob equal to HEAD,git diff HEADempty, lint rebuilt, preflight--absentfound the marker in none of 20 built files, and the tree was clean.Acceptance notes
skills/**teaches widgetfilterand reportruntimeFilterkeys that are fields of the dataset's object, which both rules accept unchanged, and no page teaches a measure name as a filter key; the one docs sentence the dimension accept widens is updated here (seat-widened file surface, that sentence only).cube.memberspelling (orders_states.state) is not given the dimension accept: the card is about bare keys, and dotted keys keep resolving as relationship paths.Verification
Derived gates (
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack) on headb498e10f: 87 families (the docs commit added the documentation families), each run and recorded asCOMMAND :: exit CODE, all 87 exit 0;--ranreconciliation: 87 derived, 87 run, 0 NOT-MEASURED, 0 UNRUN. On the code heada82a3a45the same derivation was 58 families, all exit 0. Theos-dev-reportcomment on #30006 carries the record.Human Minimum
packages/specholds no runtime logic. (packages/specuntouched.)pnpm lintis CI's.main, one pull request.packages/specnot touched.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code