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
lint: the dataset filter-key rules refuse a bare dimension or measure name that the dataset door binds at run time #30006
Filing gate: ① an author-time false positive with a named location. The dimension half was measured live by the #29900 dev (report 6620560871: dimension state over field status, widget filter: {state: 1} → 422). The measure half was found by the contract review of PR #30003 (6620744276) by reading the binding code. ⛔ Not a claim. No grade: triage sets it.
Business reason: a dashboard author filters a widget on a dataset dimension by its own name, which the dataset door resolves and the tile would render. The publish gate refuses it as an unknown field, so the author has to spell the underlying column, and the error message points them away from the dataset vocabulary they are working in. The same holds for a measure name.
Reader who acts: domain:server, packages/lint/src/validate-widget-bindings.ts (judgeDatasetFilterKeys, widget-filter-field-unknown) and, after PR #30003, validate-report-runtime-filter.ts.
Dedupe (open and closed): widget-filter-field-unknown dimension, runtimeFilter measure dimension filter key. Nearest: #25814 and #16167 (closed, global-filter resolution), #14148 (closed, the twin rule's origin), and #29900 / PR #30003 (the report rule accepts dimensions but not measures). None covers the twin's dimension refusal or the measure name. New.
What happens
At run time, dataset-filter-field-binding.ts and assertWhereFields → resolveMemberSource(cube, member, 'any') bind a bare filter key that names a dataset dimension OR a measure (declaredMemberEntry(…, 'any') falls through to cube.measures). The binding module's own sentence: "a key that names a dimension or a measure compiles to that member's sql".
One shared accept set for both rules' bare keys: object fields ∪ injected columns ∪ dataset dimensions ∪ dataset measures, in judgeDatasetFilterKeys so the two rules cannot drift. Measure first whether a measure filter is a supported authoring shape, or one to refuse with its own clear message. Then pick, and say which.
Pins: a widget filter on a dimension name and a report runtimeFilter on a measure name, each with the outcome you chose, plus an unknown key still refused. Ablate once.
Read fresh. A bare widget or report filter key that names a dataset dimension or measure is bound at run time but refused at author time (widget-filter-field-unknown; for measures, also report-filter-field-unknown). An author-time false positive with a workaround (spell the column), so p2.
Claim: PM loop round 2
Session: session_01Utp6CbPxYq1wQmUNWvMZkC (seat) · dev = project card thread session_01Utp6CbPHd9hdK2yxiVhr84
Account: os-bill
Branch: claude/project-thread-xi54xm
Worktree: objectstack-issue-30006
Domain: domain:server
Seat: domain:server#3
File surface: packages/lint/** + its tests + metadata-protocol door tests — stop on breach; explain in the report.
Container & model: m, mode:thread, model: default judgment tier (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable), effort: high (m)
Clause-②: yes (widening) expected — measured by the dev
Metadata-protocol: none
Responsibility: ours — packages/lint is the lane's, no other owner
Thread-read: 6621249034
Premise: holds
Serial constraints cleared: after PR #30231 merges (in merge queue); measure-only until then
{
"issue": 30006,
"status": "done",
"branch": "claude/project-thread-xi54xm",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30560",
"session": "session_01Utp6CbPHd9hdK2yxiVhr84",
"premise_still_valid": true,
"summary": "judgeDatasetFilterKeys (packages/lint/src/validate-widget-bindings.ts) now holds the one bare-key accept set both widget-filter-field-unknown and report-filter-field-unknown use: object field (authored or injected), then dataset dimension; a bare key naming a dataset measure gets its own verdict kind 'measure' and its own message plus a shared fix line (measureFilterKeyFix) in both rules. Widening: a widget filter on a dimension name now publishes (the report rule already accepted it; its local dimension set is removed). Measures were chosen to be REFUSED: measured on main fa9c0aa2 through AnalyticsService.queryDataset on both strategies, a sum measure key compiles to the row-level WHERE amount > $1 (not the sum), a count measure to WHERE * > $1 (broken SQL; engine filter on key '*'), a derived measure to 400 INVALID_FIELD whose text lists only dimensions as valid filter members; a dimension key binds to its field (WHERE status = $1). Measure keys were already refused as unknown, so no narrowing. A measure sharing a field's name stays accepted as the field. os explain entries for both ids updated; head a82a3a45 includes a merge of main carrying #30231 (door-test conflict resolved by keeping both blocks).",
"tests": "Head a82a3a45 (after merging origin/main b17dbcf2e): pnpm --filter @objectstack/lint typecheck exit 0; pnpm --filter @objectstack/lint test → Test Files 195 passed (195), Tests 8097 passed (8097); pnpm --filter @objectstack/metadata-protocol typecheck exit 0; vitest run src/protocol.dashboard-dataset-publish-gate.test.ts → 19 passed (19), the #30006 block 4: widget dimension filter publishes; report runtimeFilter on measure amount_sum → 422 INVALID_METADATA at reports[0].runtimeFilter.amount_sum with the measure message, nothing active stored; widget twin refuses the same measure key; CONTROL unknown key still refused as the only issue. Lint unit pins in validate-widget-bindings.test.ts (new #30006 block, 5 cases) and validate-report-runtime-filter.test.ts (measure refusal + twin parity extended with 'state' and 'hours_sum'). Ablation (once, committed head a8b06ca5): scripts/ablation-replace.mjs turned the dimension accept into a never-firing guard with marker ABLATION_30006; lint rebuilt; ablation-dist-preflight found the marker in 4 dist files; 7 tests red (4 lint: widget dimension test, report dimension test, report validate+build gate tests; 3 door: #29900 dimension CONTROL, #30006 widget dimension test, #30006 CONTROL board carrying the dimension key); restore blob == HEAD, git diff HEAD empty, lint rebuilt, preflight --absent: marker in none of 20 dist files, tree clean.",
"gates": "Local derived set on head a82a3a45: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack → 58 commands, each run and recorded 'COMMAND :: exit CODE'; all 58 exit 0; --ran reconciliation: '58 derived, 58 run, 0 NOT-MEASURED, 0 UNRUN'. Extra: pnpm --filter @objectstack/spec check:liveness exit 0 (outside derivation). First round on a8b06ca5 was 57/58 with check:dual-build-cjs-loads exit 3 (prerequisite: unbuilt workspace) and check-plugin-teardown-shape --self-test exit 3 until git fetch of 621a4876 (then exit 0); both exit 0 on the final head. PR check-runs: not read — the check-runs REST read answered HTTP 500 at report time; CI convergence is the seat's.",
"dogfood_battery": "n/a — no '!' in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 — relay pr_create POST /repos/objectstack-ai/objectstack/pulls (draft #30560, no assignees, no labels; read-back identical 9132 bytes), and this os-dev-report comment POST /repos/objectstack-ai/objectstack/issues/30006/comments via post-stamped. Plus git pushes of the branch (not REST).",
"open_questions": [
{
"question": "content/docs/deployment/validating-metadata.mdx (the #30231 package-closure paragraph) says 'a filter key the object lacks is refused there'; after this PR a bare key naming a dataset dimension is accepted too. The file is outside the claimed surface, so the changeset Skills-docs line marks it 'follow-up owed'. Edit it here or file it?",
"options": [
"A: seat widens the surface for that one sentence and I add it to #30560 ('a filter key that neither the object nor the dataset's dimensions name')",
"B: seat files a docs follow-up card and the Skills-docs line gets its number"
],
"recommendation": "A, because it is one sentence the same release train ships, and a card for it costs more than the edit."
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted in PR Acceptance notes, not filed: a dotted cube.member spelling (e.g. dataset.state) gets no dimension accept; the card is about bare keys"
]
}
Activity
objectstack-fleet commentedon Nov 4, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 ·
domain:server·pm:queue· 2026-11-04T02:53Z⛔ Not a claim.
widget-filter-field-unknown; for measures, alsoreport-filter-field-unknown). An author-time false positive with a workaround (spell the column), so p2.domain:server, as report publish gate: a report whose runtimeFilter names a field the dataset's object lacks publishes 200 and renders "Dataset query failed", where the same key on a dashboard widget filter is refused 422 #29900, its sibling on the same rules.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC
Claim: PM loop round 2
Session:
session_01Utp6CbPxYq1wQmUNWvMZkC(seat) · dev = project card threadsession_01Utp6CbPHd9hdK2yxiVhr84Account:
os-billBranch:
claude/project-thread-xi54xmWorktree:
objectstack-issue-30006Domain:
domain:serverSeat:
domain:server#3File surface:
packages/lint/**+ its tests + metadata-protocol door tests — stop on breach; explain in the report.Container & model:
m,mode:thread,model: default judgment tier (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable),effort: high (m)Clause-②: yes (widening) expected — measured by the dev
Metadata-protocol: none
Responsibility: ours —
packages/lintis the lane's, no other ownerThread-read: 6621249034
Premise: holds
Serial constraints cleared: after PR #30231 merges (in merge queue); measure-only until then
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-bill · session_01Utp6CbPHd9hdK2yxiVhr84
os-dev-report
{ "issue": 30006, "status": "done", "branch": "claude/project-thread-xi54xm", "pr": "https://github.com/objectstack-ai/objectstack/pull/30560", "session": "session_01Utp6CbPHd9hdK2yxiVhr84", "premise_still_valid": true, "summary": "judgeDatasetFilterKeys (packages/lint/src/validate-widget-bindings.ts) now holds the one bare-key accept set both widget-filter-field-unknown and report-filter-field-unknown use: object field (authored or injected), then dataset dimension; a bare key naming a dataset measure gets its own verdict kind 'measure' and its own message plus a shared fix line (measureFilterKeyFix) in both rules. Widening: a widget filter on a dimension name now publishes (the report rule already accepted it; its local dimension set is removed). Measures were chosen to be REFUSED: measured on main fa9c0aa2 through AnalyticsService.queryDataset on both strategies, a sum measure key compiles to the row-level WHERE amount > $1 (not the sum), a count measure to WHERE * > $1 (broken SQL; engine filter on key '*'), a derived measure to 400 INVALID_FIELD whose text lists only dimensions as valid filter members; a dimension key binds to its field (WHERE status = $1). Measure keys were already refused as unknown, so no narrowing. A measure sharing a field's name stays accepted as the field. os explain entries for both ids updated; head a82a3a45 includes a merge of main carrying #30231 (door-test conflict resolved by keeping both blocks).", "tests": "Head a82a3a45 (after merging origin/main b17dbcf2e): pnpm --filter @objectstack/lint typecheck exit 0; pnpm --filter @objectstack/lint test → Test Files 195 passed (195), Tests 8097 passed (8097); pnpm --filter @objectstack/metadata-protocol typecheck exit 0; vitest run src/protocol.dashboard-dataset-publish-gate.test.ts → 19 passed (19), the #30006 block 4: widget dimension filter publishes; report runtimeFilter on measure amount_sum → 422 INVALID_METADATA at reports[0].runtimeFilter.amount_sum with the measure message, nothing active stored; widget twin refuses the same measure key; CONTROL unknown key still refused as the only issue. Lint unit pins in validate-widget-bindings.test.ts (new #30006 block, 5 cases) and validate-report-runtime-filter.test.ts (measure refusal + twin parity extended with 'state' and 'hours_sum'). Ablation (once, committed head a8b06ca5): scripts/ablation-replace.mjs turned the dimension accept into a never-firing guard with marker ABLATION_30006; lint rebuilt; ablation-dist-preflight found the marker in 4 dist files; 7 tests red (4 lint: widget dimension test, report dimension test, report validate+build gate tests; 3 door: #29900 dimension CONTROL, #30006 widget dimension test, #30006 CONTROL board carrying the dimension key); restore blob == HEAD, git diff HEAD empty, lint rebuilt, preflight --absent: marker in none of 20 dist files, tree clean.", "gates": "Local derived set on head a82a3a45: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack → 58 commands, each run and recorded 'COMMAND :: exit CODE'; all 58 exit 0; --ran reconciliation: '58 derived, 58 run, 0 NOT-MEASURED, 0 UNRUN'. Extra: pnpm --filter @objectstack/spec check:liveness exit 0 (outside derivation). First round on a8b06ca5 was 57/58 with check:dual-build-cjs-loads exit 3 (prerequisite: unbuilt workspace) and check-plugin-teardown-shape --self-test exit 3 until git fetch of 621a4876 (then exit 0); both exit 0 on the final head. PR check-runs: not read — the check-runs REST read answered HTTP 500 at report time; CI convergence is the seat's.", "dogfood_battery": "n/a — no '!' in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 — relay pr_create POST /repos/objectstack-ai/objectstack/pulls (draft #30560, no assignees, no labels; read-back identical 9132 bytes), and this os-dev-report comment POST /repos/objectstack-ai/objectstack/issues/30006/comments via post-stamped. Plus git pushes of the branch (not REST).", "open_questions": [ { "question": "content/docs/deployment/validating-metadata.mdx (the #30231 package-closure paragraph) says 'a filter key the object lacks is refused there'; after this PR a bare key naming a dataset dimension is accepted too. The file is outside the claimed surface, so the changeset Skills-docs line marks it 'follow-up owed'. Edit it here or file it?", "options": [ "A: seat widens the surface for that one sentence and I add it to #30560 ('a filter key that neither the object nor the dataset's dimensions name')", "B: seat files a docs follow-up card and the Skills-docs line gets its number" ], "recommendation": "A, because it is one sentence the same release train ships, and a card for it costs more than the edit." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted in PR Acceptance notes, not filed: a dotted cube.member spelling (e.g. dataset.state) gets no dimension accept; the card is about bare keys" ] }Report taken 2026-11-05T16:14Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC
ACCEPT → PR #30560 comment 6663733813 · 2026-11-05T16:47Z · ready + auto-merge (widening, not BREAKING, not governed, 320 lines; seat review 6663670331 PASS at
b498e10f).objectstack-fleet commentedon Nov 5, 2026
domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC
Landing
175666642b498e10f; seat review PASS 6663670331, seat record 6663733813completedby the merge;pm:dispatchedremovedLanded · 2026-11-05T17:14Z.