Repository navigation
Commit 21a13f1
spec(ui): a navigation group that says nothing opens — GroupNavItem expanded defaults to true (#26427)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBWwk9BkRgZKVzh5DadYMEZV)_
Fixes #26370
Clause-②: yes
Metadata-protocol: semantic
Skills-docs: none — no skill under `skills/**` states or implies the old
default: `skills/objectstack-ui/SKILL.md` lists `expanded` on the group
row with no default, and `rules/navigation.md` writes `expanded: true`
out, so both read true under the flip, and no `objectstack-ui` eval
exercises the key. The docs half is in this diff:
`content/docs/ui/apps.mdx` (Group Navigation) now says a group opens
unless it declares `expanded: false`, and the generated
`content/docs/references/ui/app.mdx` carries the new describe text.
Ruling-ref: 6431302164 (batch #405 item 1, letter A, maintainer 「同意」)
## 维护者速读
- 改了什么:导航分组 `expanded` 的默认值从 `false` 改成 `true`,不写就展开;想折叠要写 `expanded:
false`。附一条 ADR-0087 D3 语义迁移条目、默认值变更声明和 changeset。
- 为什么改:按裁决 A,协议跟上控制台已经裁定的行为。实测:经 `defineStack` 解析的应用(showcase)以前给 8 个没写
`expanded` 的分组都下发了 `expanded: false`,控制台照此折叠;平台自带的 Setup/Account
不经解析,没写就不下发,控制台展开。两种来源结果相反,现在统一为展开。
- 风险与回滚:已编译的旧 `objectstack.json` 里写死了
`false`,那些分组仍会折叠,重新编译即可。回滚就是还原这一行默认值和迁移条目。
-
先看:`packages/spec/src/ui/app.zod.ts`(默认值与描述)、`packages/spec/src/migrations/entries/semantic/18.ui-nav-group-expanded-default-open.ts`、`packages/spec/scripts/lib/default-changes.ts`。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/ui/app.test.ts`,预期 GroupNavItemSchema 四个用例全绿:不写解析为 `true`,写 `false`
仍为 `false`。
- 需要你做的:无,席位落地。
## What changes
- `GroupNavItemSchema.expanded` (`packages/spec/src/ui/app.zod.ts`) is
now `z.boolean().default(true)`. Its describe says what each value does:
omitted opens, `false` keeps the group closed until the user opens it,
and a group holding the current route opens whatever it declares.
- ADR-0087 D3 semantic entry `ui-nav-group-expanded-default-open` (major
18). It carries the change to `os migrate meta` and the upgrade guide.
It is the same class as `list-view-edit-inline-default-on` and
`view-pagination-page-size-default-50`: no key removed, no tombstone, no
D2 conversion.
- `DEFAULT_CHANGES_BY_MAJOR[18]` declares `ui/GroupNavItem:expanded`
false → true. The spec build refused the flip until it was declared.
- Generated: `packages/spec/authorable-defaults/ui.json` (one line) and
`content/docs/references/ui/app.mdx` (the describe text, five tables).
Hand-written: one sentence in `content/docs/ui/apps.mdx`.
- Changeset: `@objectstack/spec` minor, with the two declaration lines.
- No renderer change. `ui/packages/layout/src/NavigationRenderer.tsx`
(the `explicitOpen` block, about lines 1538-1550) opens on `true` and on
an absent key and closes on `false`. After the flip the served shape is
`true` or absent for every silent group, so the served shape and the
renderer agree.
## Why
The maintainer ruled letter A on #26370 (6431302164): a navigation group
that says nothing opens, and an author who wants it closed writes
`expanded: false`, as `setup.app.ts:116` and `account.app.ts:186`
already do.
### Triage P3, measured (the hypothesis was half true)
The served navigation DOES apply the spec default, for every app that
goes through a parse. It does not for apps registered raw.
| app | producer | served groups on `origin/main` (e931621) | served
groups on this branch (2f3905e; the merge adds nothing under
`packages/spec/src/ui`) |
|:--|:--|:--|:--|
| `showcase_app` | `defineStack` (parsed; the compiled
`dist/objectstack.json` carries the same) | all 8 groups `expanded:
false`, none authored | all 8 groups `expanded: true` |
| `setup` | raw `App` object, registered without a parse | 8 groups with
no key, `group_advanced` `false` (authored) | unchanged |
| `account` | raw `App` object | inbox `true`, security `true`,
developer `false` (all authored) | unchanged |
The registry's `validate()` parse is a diagnostic only and stores the
raw item (`packages/objectql/src/registry.ts`, the spec-conformance
block), so a raw app never gains the default. `defineStack` in strict
mode returns the parsed stack, so a parsed app always does. On main this
made the two kinds of app behave oppositely in the console: the
showcase's groups arrived with a written `false` and collapsed, and
Setup's arrived with no key and opened. After the flip, both open.
## Tests
- `packages/spec/src/ui/app.test.ts`: the silent-group case now expects
`true`. New cases: an authored `false` stays `false`; a non-boolean is
refused at path `expanded`; a silent group in a parsed app opens while
an authored nested `false` stays closed.
## Acceptance notes
- The migration entry and the default-change reason name the
compiled-artifact case. A document that serialised an earlier parse
carries a written `false` and its groups stay closed until it is
recompiled or the key is deleted.
- `docs/qa/platform-checklist/areas/platform-core.json` step 2 says "QA
run found Setup's Configuration group collapsed as well". That records
an earlier run and stays true as history. Setup's Configuration group
declares no `expanded` and is served without the key, so it opens on
main and on this branch. Not edited here.
- Browser rendering was not re-measured. The open or closed reading
follows from the served shape above and the renderer's `explicitOpen`
block.
## Verification
<details><summary>Served navigation, before and after (GET
/api/v1/meta/app, showcase dev server, fresh DB, signed in as the seeded
admin)</summary>
```
origin/main e931621:
showcase_app | grp_workspace=false grp_data=false grp_slices=false grp_analytics=false grp_auth_structured=false grp_auth_viz=false grp_auth_html=false grp_auth_react=false
setup | group_overview=absent group_apps=absent group_people_org=absent group_access_control=absent group_approvals=absent group_configuration=absent group_diagnostics=absent group_integrations=absent group_advanced=false
account | grp_account_inbox=true grp_account_security=true grp_account_developer=false
branch 2f3905e:
showcase_app | grp_workspace=true grp_data=true grp_slices=true grp_analytics=true grp_auth_structured=true grp_auth_viz=true grp_auth_html=true grp_auth_react=true
setup | (identical to main)
account | (identical to main)
```
</details>
<details><summary>Local runs at head 061f21a (the merge of origin/main
e677770; no commit since)</summary>
```
pnpm --filter @objectstack/spec typecheck exit 0
pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 exit 0 · Test Files 806 passed (806) · Tests 23291 passed | 1 todo
pnpm --filter @objectstack/spec build && check:generated exit 0 · All 14 generated artifacts are up to date
node scripts/pm/dispatch-gates.mjs --commands 116 commands, each run once, exit code captured before any pipe: 116 x exit 0
node scripts/pm/dispatch-gates.mjs --ran ran.list exit 0 · 116 derived, 116 run, 0 NOT-MEASURED, 0 UNRUN
```
Before the merge, at 2f3905e: the same spec suite (805 files, 23266
tests) and the same 116 gates were green, after two
precondition refusals were cleared (a fixture commit the shallow clone
lacked; a spec dist rebuilt mid-read).
The spec build refused the flip once until `DEFAULT_CHANGES_BY_MAJOR`
declared it (`ui/GroupNavItem:expanded: false -> true`),
and `check:generated --fix` regenerated `check:docs` only.
`packages/cli` `migrate-meta-engine-guidance` (integration tier; reads
every semantic entry for tracker ids) is declared to
CI's Test Core. The new entry's printed fields carry 0 tracker ids
(scanned).
</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.
- [ ] 3. No formatter ran. `pnpm lint` was not run locally: the
repo-wide scan is CI's (`Lint & Repo Gates`).
- [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.
- [ ] The *Before Submitting* list in CONTRIBUTING.md: not walked item
by item; the local runs below are what was done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WGufzgtoAxAMDnavXSKysG)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d0a7e74 commit 21a13f1
9 files changed
Lines changed: 143 additions & 9 deletions
File tree
- .changeset
- content/docs
- references/ui
- ui
- packages/spec
- authorable-defaults
- scripts/lib
- src
- migrations/entries/semantic
- ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
286 | 286 | | |
287 | 287 | | |
288 | 288 | | |
289 | | - | |
| 289 | + | |
290 | 290 | | |
291 | 291 | | |
292 | 292 | | |
| |||
475 | 475 | | |
476 | 476 | | |
477 | 477 | | |
478 | | - | |
| 478 | + | |
479 | 479 | | |
480 | 480 | | |
481 | 481 | | |
| |||
680 | 680 | | |
681 | 681 | | |
682 | 682 | | |
683 | | - | |
| 683 | + | |
684 | 684 | | |
685 | 685 | | |
686 | 686 | | |
| |||
879 | 879 | | |
880 | 880 | | |
881 | 881 | | |
882 | | - | |
| 882 | + | |
883 | 883 | | |
884 | 884 | | |
885 | 885 | | |
| |||
1200 | 1200 | | |
1201 | 1201 | | |
1202 | 1202 | | |
1203 | | - | |
| 1203 | + | |
1204 | 1204 | | |
1205 | 1205 | | |
1206 | 1206 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
143 | 143 | | |
144 | 144 | | |
145 | 145 | | |
146 | | - | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
147 | 149 | | |
148 | 150 | | |
149 | 151 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
50 | | - | |
| 50 | + | |
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
667 | 667 | | |
668 | 668 | | |
669 | 669 | | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
670 | 684 | | |
671 | 685 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
82 | | - | |
| 82 | + | |
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
208 | 208 | | |
209 | 209 | | |
210 | 210 | | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
211 | 224 | | |
212 | 225 | | |
213 | 226 | | |
214 | 227 | | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
215 | 257 | | |
216 | 258 | | |
217 | 259 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
808 | 808 | | |
809 | 809 | | |
810 | 810 | | |
811 | | - | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
812 | 817 | | |
813 | 818 | | |
814 | 819 | | |
| |||
0 commit comments