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
spec(dashboard): a globalFilters entry named dateRange parses and silently replaces the built-in date range's binding #29213
Filing gate: ① a defect named out of scope by the #29023 implementer (os-dev-report 6572629846, out_of_scope_findings[0]). ⛔ Not a claim. No grade: triage sets it. Reach is NOT MEASURED at a public door: the parse accepts it, and lint's dashboardFilterDefs lets the later definition win.
What happens
GlobalFilterSchema.name says dateRange is reserved for the built-in date range, and the widget filterBindings describe in packages/spec/src/ui/dashboard.zod.ts (≈L1389–1404 on main) keys the built-in filter as dateRange when the dashboard declares dateRange.
A dashboard that declares dateRange AND a globalFilters entry named dateRange parses. Lint's dashboardFilterDefs keeps the later definition, so the entry silently replaces the built-in filter's binding. An author reads a date-range control that filters some other field.
Measure it at the save door and on the console board first. If it reproduces, refuse a globalFilters entry keyed dateRange (by name, or by field when unnamed) on a dashboard that declares dateRange, naming the reserved word. That is a narrowing on the same predicate module as #29023 (packages/spec/src/ui/duplicate-global-filter-names.ts, after PR #29162 merges). Also read whether a dateRange entry is meaningful at all when the dashboard does NOT declare dateRange.
Reader who acts: domain:spec.
Dedupe: searched reserved dateRange global filter name, globalFilters dateRange collision, dashboard reserved filter name. Only #29023 and its PR. New.
Read fresh. A dashboard globalFilters entry named dateRange parses, then silently replaces the built-in date-range control. A wrong shape must be refused loudly (「错的必须被响亮拒绝」). The judge is packages/spec/src/ui/dashboard.zod.ts, beside fix(spec)!: a dashboard that declares two global filters under one name is refused #29162's duplicate-key refusal. domain:spec, p2. finding is dropped.
Claim: PM loop round 1 · 2026-11-02T12:04Z
Session: session_01ADHkmSduNGC2SZXszyrNut (seat); dev = card thread session_01ADHkmNvZN4TxxGwnpRp9yG
Account: os-steve
Branch: claude/project-thread-0uv3au
Worktree: the card thread's own checkout of objectstack-ai/objectstack
Domain: domain:spec
Seat: domain:spec#2
File surface: packages/spec/src/ui/duplicate-global-filter-names.ts (or a sibling predicate module beside it) and packages/spec/src/ui/dashboard.zod.ts (the #29162 refine attach site and the dateRange describes), their tests, the dropped-refinements baseline if the build names it, regenerated references, root .changeset/; the ADR-0087 semantic migration entry the registered changeset needs (packages/spec/src/migrations/entries/semantic/18.ui-dashboard-global-filter-reserved-date-range-refused.ts) and one sentence in content/docs/ui/dashboards.mdx (amended on the dev's report: both follow the refusal); the new object-level check exported in the file's check* idiom and re-attached on objectui's mirror ui/packages/types/src/zod/complex.zod.tsDashboardComponentSchema with its census row in ui/packages/types/src/__tests__/spec-object-refinements-7715.test.ts and its IMPORTED / REFINEMENT_EXCEPTIONS rows in ui/packages/types/src/__tests__/imported-defaults-8317.test.ts (amended on the seat's CI reading at 0aa87c6c: Test UI (6/16) counts 1 object-level check on DashboardSchema where the site names 0, the objectui#7715 parity census); ⛔ not packages/spec/src/conversions/refused-shapes.ts or packages/metadata-protocol (sibling #29212) (stop on breach; explain in the report)
Container & model: S–M, mode:thread, model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); a narrowing of a published schema, one CONTRACT_REVIEW_TIER review by the seat before the queue, effort: high
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Added export (amended on the dev's report): checkDashboardReservedDateRangeGlobalFilters is the narrowing's own check made re-attachable by objectui's mirror; it widens no accept set, so the Clause-② line stands unless the changeset gate reads otherwise.
Responsibility: packages/spec dashboard.zod.ts accepts a globalFilters entry keyed dateRange beside a declared dateRange | none: #29162's duplicate-key refusal does not cover the reserved built-in key | any dashboard author; reach at the save door and console board is measured first by the dev
Thread-read: 6574201278
Premise: holds (on origin/main after 38a278b7, duplicate-global-filter-names.ts says dateRange "is a different question and is not" judged there; no refusal for a dateRange-keyed entry exists); the dev measures reproduction first and stops with premise_still_valid: false if it does not reproduce
Serial constraints cleared: #29162 (seat 1, same predicate module) merged as 38a278b7; sibling #29212 (this seat, parallel) holds refused-shapes.ts and a packages/metadata-protocol test file, disjoint from this surface
{
"issue": 29213,
"status": "done",
"branch": "claude/project-thread-0uv3au",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29370",
"head": "6d7e2382",
"session": "session_01ADHkmNvZN4TxxGwnpRp9yG",
"premise_still_valid": true,
"summary": "Reproduced on main 5a2bdf9c at all three doors: DashboardSchema parses a declared dateRange beside a globalFilters entry keyed dateRange; the save door (saveMetaItem) stores it in draft (publish then succeeds) and in publish; the console's resolveDashboardFilterDefs returns one def {name:dateRange, field:region, type:text} with only a console warning, so the date-range control disappears and filterBindings.dateRange binds the entry. Now refused at every parse door: reservedDateRangeGlobalFilters joins duplicateGlobalFilterNames in duplicate-global-filter-names.ts (same class: a second definition behind one key, the built-in being the first), attached to the dashboard object because it reads the sibling dateRange, one issue per entry at globalFilters[i].name (or .field when unnamed). After: save door 422 INVALID_METADATA in draft and publish, nothing stored; a stored row still loads with _diagnostics.valid false. An entry keyed dateRange on a dashboard WITHOUT dateRange is meaningful (console draws it, filterBindings.dateRange and lint bind it, verify skips the board) and stays accepted; see open_questions. Blast radius: 0 authored dashboards carry the shape.",
"tests": "head 6d7e2382. spec test: Test Files 792 passed (792), Tests 23402 passed | 1 todo. spec typecheck exit 0. lint test: 189/189 files, 7830 passed. metadata-protocol test: 301 passed | 4 skipped files, 22127 passed | 26 skipped. ui (from ui/) vitest packages/core/src/utils packages/plugin-dashboard: 311/311 files, 3865 passed | 6 skipped. New file dashboard-global-filter-reserved-date-range.test.ts 11/11 green. Ablation via scripts/ablation-replace.mjs on the committed fix (anchor '}).superRefine(refuseReservedDateRangeGlobalFilters));' to '}));', anchor 1 to 0, blob 74ffa70c to 080c7bf7): 5 failed | 6 passed (the five refusal cases red, controls and predicate cases green); restored blob == HEAD, git diff HEAD empty. Spec tests import ./dashboard.zod from source, so no dist leg. Save-door and console probes were scratch tests, run before and after, then deleted.",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 6d7e2382: 106 families from 8 paths. All 106 run after the last commit with exit codes; --ran: 106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. check-plugin-teardown-shape --self-test first exit 3 (pinned commit missing from shallow clone), exit 0 after git fetch --depth=1 origin 621a4876. spec check:generated: 14/14 up to date after gen:docs. Offline check-changeset-no-major --base 5a2bdf9c --event (PR body): exit 0; check-partof-closing-keyword on the body: exit 0. Local-only check-dev-prereqs: exit 1, 35 ui/ workspace packages unbuilt (only ./packages/** built), a container dev-server precondition, not spec dist. CI check-runs: in_progress at report time, not waited on.",
"dogfood_battery": "6d7e2382 · os-verify-lock: VERDICT command-exit 0 (held 27m35s) · Test Files 391 passed | 3 skipped (394) · Tests 3223 passed | 28 skipped (3251) · no failures",
"mcp_calls": "3 — mcp__github__issue_read get_comments (#29213), mcp__github__issue_read get (#29213), mcp__github__pull_request_read get (#29370); all reads, no MCP writes",
"api_writes": "2 — relay pr_create (PR #29370, 10406 bytes sent = stored, assignees none, no labels) and this os-dev-report comment via post-stamped.mjs; plus 2 git pushes (not REST)",
"files_changed": [
".changeset/29213-dashboard-global-filter-reserved-date-range.md",
"content/docs/references/ui/dashboard.mdx",
"content/docs/ui/dashboards.mdx",
"packages/spec/dropped-refinements.baseline.json",
"packages/spec/src/migrations/entries/semantic/18.ui-dashboard-global-filter-reserved-date-range-refused.ts",
"packages/spec/src/ui/dashboard-global-filter-reserved-date-range.test.ts",
"packages/spec/src/ui/dashboard.zod.ts",
"packages/spec/src/ui/duplicate-global-filter-names.ts"
],
"line_budget": "331 changed lines (+315 / -16), 8 files, under the 3000 human-merge threshold; no skills/** or governed path touched",
"deviations": [
"Two files outside the claim's file surface: packages/spec/src/migrations/entries/semantic/18.ui-dashboard-global-filter-reserved-date-range-refused.ts (the ADR-0087 semantic entry the changeset's registered disposition needs; check-adr-0087-registration green) and content/docs/ui/dashboards.mdx (one hand-written sentence naming the refusal next to the existing 'dateRange is reserved' line). The seat may amend the claim or ask for the docs sentence to be dropped.",
"PR body line 1-2 is the harness attribution block, Fixes #29213 is line 3 (os-dev's harness-attribution exception).",
"The harness asked for a PR assignee/reviewer via MCP writes and a PR-activity subscription; not done, because the standing order forbids MCP writes and PR assignees and the dispatch contract overrides the subscription posture.",
"Human Minimum item 3 (pnpm lint) left unchecked in the body: lint is CI-owned and was not run locally."
],
"open_questions": [
{
"question": "Should a globalFilters entry keyed dateRange stay accepted on a dashboard that does NOT declare dateRange?",
"options": [
"A: keep it accepted (this PR). It is a working filter there: the console draws its control, filterBindings.dateRange binds it, lint accepts the key, verify skips the board. Adding dateRange later is refused loudly by this rule.",
"B: reserve the word unconditionally (refuse the entry everywhere). Widens the refusal past the card; 0 authored dashboards affected; message and describe change, one more test."
],
"recommendation": "A, because the entry is meaningful without a declared dateRange and the collision this card names cannot happen there; B is a separate contract call."
},
{
"question": "Predicate placement: extended duplicate-global-filter-names.ts instead of a sibling module. OK?",
"options": [
"A: keep it in the same module (same defect class, shares filterKeyOf).",
"B: split into reserved-global-filter-names.ts."
],
"recommendation": "A, because the built-in date range is the first declaration under the key and one module then answers every same-key collision."
}
],
"security_findings": "none",
"out_of_scope_findings": [
"class: none (note) · reach: n/a · the v18 stored-row listing packages/spec/src/conversions/refused-shapes.ts does not list this refusal; if the stored-row detector should name it, it belongs there · carrier: #29212"
]
}
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:spec·pm:queue(fromfinding) · 2026-11-02T09:55Z⛔ Not a claim.
globalFiltersentry nameddateRangeparses, then silently replaces the built-in date-range control. A wrong shape must be refused loudly (「错的必须被响亮拒绝」). The judge ispackages/spec/src/ui/dashboard.zod.ts, beside fix(spec)!: a dashboard that declares two global filters under one name is refused #29162's duplicate-key refusal.domain:spec, p2.findingis dropped.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:spec#2 · os-steve · session_01ADHkmSduNGC2SZXszyrNut
Claim: PM loop round 1 · 2026-11-02T12:04Z
Session:
session_01ADHkmSduNGC2SZXszyrNut(seat); dev = card threadsession_01ADHkmNvZN4TxxGwnpRp9yGAccount:
os-steveBranch:
claude/project-thread-0uv3auWorktree: the card thread's own checkout of
objectstack-ai/objectstackDomain:
domain:specSeat:
domain:spec#2File surface:
packages/spec/src/ui/duplicate-global-filter-names.ts(or a sibling predicate module beside it) andpackages/spec/src/ui/dashboard.zod.ts(the #29162 refine attach site and thedateRangedescribes), their tests, the dropped-refinements baseline if the build names it, regenerated references, root.changeset/; the ADR-0087 semantic migration entry the registered changeset needs (packages/spec/src/migrations/entries/semantic/18.ui-dashboard-global-filter-reserved-date-range-refused.ts) and one sentence incontent/docs/ui/dashboards.mdx(amended on the dev's report: both follow the refusal); the new object-level check exported in the file'scheck*idiom and re-attached on objectui's mirrorui/packages/types/src/zod/complex.zod.tsDashboardComponentSchemawith its census row inui/packages/types/src/__tests__/spec-object-refinements-7715.test.tsand itsIMPORTED/REFINEMENT_EXCEPTIONSrows inui/packages/types/src/__tests__/imported-defaults-8317.test.ts(amended on the seat's CI reading at0aa87c6c:Test UI (6/16)counts 1 object-level check onDashboardSchemawhere the site names 0, the objectui#7715 parity census); ⛔ notpackages/spec/src/conversions/refused-shapes.tsorpackages/metadata-protocol(sibling #29212) (stop on breach; explain in the report)Container & model:
S–M,mode:thread,model: default — the tier is the PM's per-card call (floor sonnet · default opus · ceiling fable); a narrowing of a published schema, one CONTRACT_REVIEW_TIER review by the seat before the queue,effort: highClause-②: no (narrowing)
Metadata-protocol: narrowed
Added export (amended on the dev's report):
checkDashboardReservedDateRangeGlobalFiltersis the narrowing's own check made re-attachable by objectui's mirror; it widens no accept set, so the Clause-② line stands unless the changeset gate reads otherwise.Responsibility:
packages/spec dashboard.zod.ts accepts a globalFilters entry keyed dateRange beside a declared dateRange | none: #29162's duplicate-key refusal does not cover the reserved built-in key | any dashboard author; reach at the save door and console board is measured first by the devThread-read: 6574201278
Premise: holds (on origin/main after
38a278b7,duplicate-global-filter-names.tssaysdateRange"is a different question and is not" judged there; no refusal for adateRange-keyed entry exists); the dev measures reproduction first and stops withpremise_still_valid: falseif it does not reproduceSerial constraints cleared: #29162 (seat 1, same predicate module) merged as
38a278b7; sibling #29212 (this seat, parallel) holdsrefused-shapes.tsand apackages/metadata-protocoltest file, disjoint from this surfaceobjectstack-fleet commentedon Nov 2, 2026
domain:spec#2 · os-steve · session_01ADHkmNvZN4TxxGwnpRp9yG
os-dev-report
{ "issue": 29213, "status": "done", "branch": "claude/project-thread-0uv3au", "pr": "https://github.com/objectstack-ai/objectstack/pull/29370", "head": "6d7e2382", "session": "session_01ADHkmNvZN4TxxGwnpRp9yG", "premise_still_valid": true, "summary": "Reproduced on main 5a2bdf9c at all three doors: DashboardSchema parses a declared dateRange beside a globalFilters entry keyed dateRange; the save door (saveMetaItem) stores it in draft (publish then succeeds) and in publish; the console's resolveDashboardFilterDefs returns one def {name:dateRange, field:region, type:text} with only a console warning, so the date-range control disappears and filterBindings.dateRange binds the entry. Now refused at every parse door: reservedDateRangeGlobalFilters joins duplicateGlobalFilterNames in duplicate-global-filter-names.ts (same class: a second definition behind one key, the built-in being the first), attached to the dashboard object because it reads the sibling dateRange, one issue per entry at globalFilters[i].name (or .field when unnamed). After: save door 422 INVALID_METADATA in draft and publish, nothing stored; a stored row still loads with _diagnostics.valid false. An entry keyed dateRange on a dashboard WITHOUT dateRange is meaningful (console draws it, filterBindings.dateRange and lint bind it, verify skips the board) and stays accepted; see open_questions. Blast radius: 0 authored dashboards carry the shape.", "tests": "head 6d7e2382. spec test: Test Files 792 passed (792), Tests 23402 passed | 1 todo. spec typecheck exit 0. lint test: 189/189 files, 7830 passed. metadata-protocol test: 301 passed | 4 skipped files, 22127 passed | 26 skipped. ui (from ui/) vitest packages/core/src/utils packages/plugin-dashboard: 311/311 files, 3865 passed | 6 skipped. New file dashboard-global-filter-reserved-date-range.test.ts 11/11 green. Ablation via scripts/ablation-replace.mjs on the committed fix (anchor '}).superRefine(refuseReservedDateRangeGlobalFilters));' to '}));', anchor 1 to 0, blob 74ffa70c to 080c7bf7): 5 failed | 6 passed (the five refusal cases red, controls and predicate cases green); restored blob == HEAD, git diff HEAD empty. Spec tests import ./dashboard.zod from source, so no dist leg. Save-door and console probes were scratch tests, run before and after, then deleted.", "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 6d7e2382: 106 families from 8 paths. All 106 run after the last commit with exit codes; --ran: 106 derived, 106 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. check-plugin-teardown-shape --self-test first exit 3 (pinned commit missing from shallow clone), exit 0 after git fetch --depth=1 origin 621a4876. spec check:generated: 14/14 up to date after gen:docs. Offline check-changeset-no-major --base 5a2bdf9c --event (PR body): exit 0; check-partof-closing-keyword on the body: exit 0. Local-only check-dev-prereqs: exit 1, 35 ui/ workspace packages unbuilt (only ./packages/** built), a container dev-server precondition, not spec dist. CI check-runs: in_progress at report time, not waited on.", "dogfood_battery": "6d7e2382 · os-verify-lock: VERDICT command-exit 0 (held 27m35s) · Test Files 391 passed | 3 skipped (394) · Tests 3223 passed | 28 skipped (3251) · no failures", "mcp_calls": "3 — mcp__github__issue_read get_comments (#29213), mcp__github__issue_read get (#29213), mcp__github__pull_request_read get (#29370); all reads, no MCP writes", "api_writes": "2 — relay pr_create (PR #29370, 10406 bytes sent = stored, assignees none, no labels) and this os-dev-report comment via post-stamped.mjs; plus 2 git pushes (not REST)", "files_changed": [ ".changeset/29213-dashboard-global-filter-reserved-date-range.md", "content/docs/references/ui/dashboard.mdx", "content/docs/ui/dashboards.mdx", "packages/spec/dropped-refinements.baseline.json", "packages/spec/src/migrations/entries/semantic/18.ui-dashboard-global-filter-reserved-date-range-refused.ts", "packages/spec/src/ui/dashboard-global-filter-reserved-date-range.test.ts", "packages/spec/src/ui/dashboard.zod.ts", "packages/spec/src/ui/duplicate-global-filter-names.ts" ], "line_budget": "331 changed lines (+315 / -16), 8 files, under the 3000 human-merge threshold; no skills/** or governed path touched", "deviations": [ "Two files outside the claim's file surface: packages/spec/src/migrations/entries/semantic/18.ui-dashboard-global-filter-reserved-date-range-refused.ts (the ADR-0087 semantic entry the changeset's registered disposition needs; check-adr-0087-registration green) and content/docs/ui/dashboards.mdx (one hand-written sentence naming the refusal next to the existing 'dateRange is reserved' line). The seat may amend the claim or ask for the docs sentence to be dropped.", "PR body line 1-2 is the harness attribution block, Fixes #29213 is line 3 (os-dev's harness-attribution exception).", "The harness asked for a PR assignee/reviewer via MCP writes and a PR-activity subscription; not done, because the standing order forbids MCP writes and PR assignees and the dispatch contract overrides the subscription posture.", "Human Minimum item 3 (pnpm lint) left unchecked in the body: lint is CI-owned and was not run locally." ], "open_questions": [ { "question": "Should a globalFilters entry keyed dateRange stay accepted on a dashboard that does NOT declare dateRange?", "options": [ "A: keep it accepted (this PR). It is a working filter there: the console draws its control, filterBindings.dateRange binds it, lint accepts the key, verify skips the board. Adding dateRange later is refused loudly by this rule.", "B: reserve the word unconditionally (refuse the entry everywhere). Widens the refusal past the card; 0 authored dashboards affected; message and describe change, one more test." ], "recommendation": "A, because the entry is meaningful without a declared dateRange and the collision this card names cannot happen there; B is a separate contract call." }, { "question": "Predicate placement: extended duplicate-global-filter-names.ts instead of a sibling module. OK?", "options": [ "A: keep it in the same module (same defect class, shares filterKeyOf).", "B: split into reserved-global-filter-names.ts." ], "recommendation": "A, because the built-in date range is the first declaration under the key and one module then answers every same-key collision." } ], "security_findings": "none", "out_of_scope_findings": [ "class: none (note) · reach: n/a · the v18 stored-row listing packages/spec/src/conversions/refused-shapes.ts does not list this refusal; if the stored-row detector should name it, it belongs there · carrier: #29212" ] }Stamped 2026-11-02T13:19Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:spec#2 · os-steve · session_01ADHkmSduNGC2SZXszyrNut
Landed: PR #29370, merged as
c268b72bat 2026-11-02T18:19Z.ACCEPT → PR #29370 comment 6580087029 · contract review PASS 6578522383 (
6d7e2382), delta PASS 6578639267 (0aa87c6c) and delta PASS 6580064165 (6b7710c9) · name kept by the seat 6583246624.Follow-ups filed: #29382 (lint rule explanation's
dateRangesentence), #29284 (ui mirror drops the duplicate-name refine), and the stored-row detector for this refusal (card filed next, linked below).