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
Commit a5fcd06
Browse filesBrowse the repository at this point in the historyBrowse files
fix(spec): os migrate meta --stored lists a stored dashboard whose global filter reuses the reserved dateRange key (#29720)
domain:spec#2 · os-support-ai · session_01WGufzhJG3tKnksHVa1krBz
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBY61hHdLdFdzwXbxaNtv58y)_Fixes#29541
Supersedes #29586 (same commits, resumed on a new branch, merged with
main after #29589; #29586 is closed)
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:`os migrate meta --stored` 现在会列出「看板声明了内置 `dateRange`,又有一个全局筛选器也叫
`dateRange`」的已存数据行;两条看板筛选器迁移条目的验收句都补上了「存量预览会列出这种行」。
- 为什么改:#29213 已让每道门拒收这种看板,但升级前的存量预览不报它,运维升级前看不到哪些看板下次保存会被拒。
- 风险与回滚:只读检测,任何入口的接受/拒收结果都不变,不改写任何行;回滚即 revert 本 PR。
- 先看:`packages/spec/src/conversions/refused-shapes.ts` 的
`dashboardReservedDateRangeGlobalFilter`(调用拒收门同一个判定函数,与 #29589 的
`objectExtensionFieldPatchKeyGrammar` 并存),再看 `refused-shapes.test.ts` 末尾
#29541 五条。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/conversions/refused-shapes.test.ts`,预期 92 个测试全过(含 #29554 的四条),其中
#29541 五条。
- 需要你做的:无,席位落地。
## What changes
- **Stored listing.** `REFUSED_STORED_SHAPES` gains
`dashboardReservedDateRangeGlobalFilter` (id
`ui-dashboard-global-filter-reserved-date-range-refused`, the #29213 D3
entry of step 18), registered right after
`dashboardDuplicateGlobalFilterName`. It walks `stack.dashboards[]` and
calls `reservedDateRangeGlobalFilters(dashboard.dateRange,
dashboard.globalFilters)` from `ui/duplicate-global-filter-names.ts`,
the predicate `DashboardSchema`'s refinement reads; no logic is copied.
One TODO per entry, at `dashboards[i].globalFilters[j].name` (or
`.field` when the entry has no `name`), which is the site the parse
refuses. `from` carries the reserved key and the index only. The reason
leads with the fate (no lossless rewrite, loads with
`_diagnostics.valid: false`, next save refused 422) and ends with the
predicate's own message.
- **Upgrade guide entries.** `acceptanceCriteria` of
`18.ui-dashboard-global-filter-reserved-date-range-refused.ts` and of
`18.ui-dashboard-global-filter-duplicate-name-refused.ts` (#29023) each
gain one sentence: `os migrate meta --stored` lists each such stored
dashboard under the entry's id, at the site the parse refuses.
- **Docblock.** The `refused-shapes.ts` header's list of borrowed
predicates names `reservedDateRangeGlobalFilters`.
- **Changeset.**
`.changeset/29541-reserved-date-range-stored-detector.md`,
`@objectstack/spec` patch.
## Why
The refusal's D3 entry already promised that a stored row with the pair
loads as invalid and is refused on its next save, but the stored preview
read such a row as already on protocol. An operator previewing the
upgrade saw nothing.
## Tests
`refused-shapes.test.ts` gains five cases under `#29541`, and the roster
pin names the new id:
- the card shape (a declared `dateRange` beside an entry named
`dateRange`): one TODO at `dashboards[0].globalFilters[0].name` with the
reserved word, the built-in date range and the predicate's message; the
row is not rewritten, and the parse still refuses the same site;
- an entry with no `name` and `field: 'dateRange'`: listed once at
`.field`, as the parse locates it;
- two entries both named `dateRange`: listed under each refusal (one
duplicate-key TODO and two reserved-key TODOs), exactly the three
`custom` issues the parse raises;
- CONTROL: with no `dateRange` declared, an entry keyed `dateRange` (by
name or by field) is not listed and the parse accepts it;
- CONTROL: beside a declared `dateRange`, a named entry whose `field` is
`dateRange` is not listed and parses.
Ablation (fix committed first; `scripts/ablation-replace.mjs --delete`
on the registry line ` dashboardReservedDateRangeGlobalFilter,`): Tests
4 failed | 84 passed (88). The roster pin and the three listing cases
went red, both controls stayed green. Restored: blob after restore
equals HEAD, `git diff HEAD` empty. The spec suite imports
`./refused-shapes.js` from source, so no dist was involved.
## Acceptance notes
- Premise measured on `origin/main` @ `ff827bcf` before the change:
`applyConversionsToStoredItem('dashboard', row, { onTodo })` for a
declared `dateRange` beside an entry named `dateRange` gave 0 TODOs
while the parse refused `globalFilters.0.name`. The control, two filters
named `r`, gave 1 (`ui-dashboard-global-filter-duplicate-name-refused`).
- `duplicate-global-filter-names.ts` and `dashboard.zod.ts` are
untouched. No ledger moved. The detector adds no new export, so
`api-surface` did not change.
- No save-door pin is added in `packages/metadata-protocol`. The
sibling's PR added one; this order's file surface names none, and
#29370's spec test covers the refusal in each door's form.
- Serial: #29554's PR #29589 (`57958aab`) edited the same
`refused-shapes.ts` and merged first. The merge of `origin/main` @
`3d5933a9` conflicted in `refused-shapes.ts` and its test and was
resolved additively: #29589's `refusedFieldPatchKeys` /
`objectExtensionFieldPatchKeyGrammar` (and its `ObjectExtensionSchema`
import) stay, this card's dashboard detector stays, the header docblock
names both borrowed predicates, and `REFUSED_STORED_SHAPES` lists
`dashboardReservedDateRangeGlobalFilter` then
`objectExtensionFieldPatchKeyGrammar`. The roster pin counts both new
ids. No generated path moved on either side, so `os-regen-merge.sh` had
nothing to take.
## Verification
Head `76d25fa9`: the commits of #29586 (through `9e7c4c35`) plus one
merge of `origin/main` at `3d5933a9`. Every reading below was taken on
that head. The readings of the old head are in #29586.
<details><summary>Package readings</summary>
- `pnpm --filter @objectstack/spec test` + `typecheck` under
`scripts/pm/os-verify-lock.sh`: Test Files 802 passed | 1 skipped (803),
Tests 23789 passed | 1 skipped | 1 todo (23791); typecheck exit 0
(migration registry current: 535 semantic). Lock verdict command-exit 0.
- `refused-shapes.test.ts` alone: 92 passed (92), the 88 of #29586 plus
#29554's four.
- `pnpm build` (whole repo, under the lock): 117 of 117 tasks, exit 0.
</details>
<details><summary>Derived gates</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 80 families from 5 paths (144
changed lines vs merge base `3d5933a9`). Each was run on this head.
`--ran`: 80 derived, 80 run, 0 NOT-MEASURED, 0 UNRUN.
- `check-changeset-no-major.mjs --base origin/main` and
`check-adr-0087-registration.mjs --base origin/main` exit 0 on the
merged head with the unchanged declarations (`Clause-②: no`,
`Metadata-protocol: none`, `@objectstack/spec` patch).
- Eleven families first answered exit 3 (no `dist/`); after the full
build each answered exit 0.
- CI families are read from this PR's check-runs. Dogfood is not owed:
no `!` in the title and no `(narrowing)` arm. The at-tier contract
review on this head goes to triage, per the claim.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [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: its generated artifacts are
regenerated and committed.
- [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.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzg4LNpBsJKihJRTLqD
Co-authored-by: Claude <noreply@anthropic.com>
fix(spec): `os migrate meta --stored` lists a stored dashboard whose global filter is keyed `dateRange` beside a declared `dateRange`
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
-**Before.**`DashboardSchema` refuses a dashboard that declares the built-in `dateRange` and also keys a `globalFilters` entry `dateRange` (its `name`, or its `field` when it has no `name`), and the dashboard save door answers 422 `INVALID_METADATA`. A dashboard already stored in that shape still loads with `_diagnostics` reading `valid: false`, but the v18 stored-row preview had no detector for it, so `os migrate meta --stored` named no such row before the upgrade.
11
+
-**Now.** The preview lists each such stored dashboard under the TODO id `ui-dashboard-global-filter-reserved-date-range-refused` (the refusal's upgrade guide entry), at the entry's `name` (or `field`), the site the parse refuses, with the refusal's own message. The detector calls the refinement's own predicate, so a row is listed exactly when the parse refuses it. Nothing rewrites it: whether the entry was meant to be the date range or a filter of its own, only the author knows. The upgrade guide entries of this refusal and of the duplicate-key refusal now say that `os migrate meta --stored` lists such a row.
12
+
-**Not changed.** What any door accepts or refuses. On a dashboard that does not declare `dateRange`, an entry keyed `dateRange` is not listed; nor is a named entry whose `field` is `dateRange`.
Copy file name to clipboardExpand all lines: packages/spec/src/migrations/entries/semantic/18.ui-dashboard-global-filter-reserved-date-range-refused.ts
0 commit comments