Repository navigation
Commit c064d3a
perf(console): write app navigation at page and dashboard create and delete sites (#28783)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbNiiAtyW6uY3eTDyWYkBLff)_
Fixes #28680
Fixes #28645
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:页面或仪表盘在控制台被创建(首次发布)或删除时,由该处直接写入应用导航;`NavigationSyncEffect`
不再在每个应用页读取整张 `/meta/page` 和 `/meta/dashboard` 列表做差异比较。
- 为什么改:这是 #28645 的第二步,按维护者裁定 A(#28680)。去掉这两次列表读取后,页面路由 26 → 24
次请求,对象列表路由 33 → 31。
- 风险与回滚:在本标签页之外创建或删除的无包页面/仪表盘(服务端智能体、另一个标签页、直接调用
REST)不再自动进入导航,只能由其写入方处理;包安装和 CLI 部署的条目带 `_packageId`,原本就不经过差异比较。回滚即还原本
PR,无数据或协议变更。
-
先看:`ui/packages/app-shell/src/hooks/useNavigationSync.ts`(`useNavigationWrites`)、`ui/packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx`(`doPublish`、`runReset`)、`ui/packages/app-shell/src/preview/usePublishAllDrafts.ts`。
- 两分钟验证:`cd ui && pnpm exec vitest run
packages/app-shell/src/views/metadata-admin/ResourceEditPage.navigationWrites-28680.test.tsx
packages/app-shell/src/hooks/__tests__/useNavigationSync.test.tsx`,预期 22
个用例全绿。
- 需要你做的:无,席位落地。仪表盘路由仍读 `/meta/dashboard`(见 Deviations),可另开卡处理。
## What changes
`NavigationSyncEffect` read the whole `/meta/page` and `/meta/dashboard`
lists on every app page, after the first paint, only to diff their name
sets and add or remove navigation entries. Maintainer ruling A on #28680
retires it. The console's sites now write navigation themselves, through
a new door in `useNavigationSync.ts`:
- **`useNavigationWrites()`** returns `created(type, name, item?)` and
`deleted(type, name, item?)`. They call the existing
`sync…CreatedAllApps` / `sync…DeletedAllApps` writers and keep the
diff's rules: only a `page` or a `dashboard`; never a platform artifact
(`isPlatformArtifact`: a `sys_` name, or `_packageId` / `_provenance:
'package'`); nothing in draft preview (ADR-0037, where `apps` may be
draft bodies); only into apps that may be written
(`isNavigationSyncableApp`). Entries are written label-less, as before
(objectui#9868).
- **The metadata editor's Publish** (`ResourceEditPage` `doPublish`):
when the item held no layer before the publish (its draft was its only
layer, `/layers` 404) and the editor is not bound to a package, the page
or dashboard is created and joins navigation. A later publish of the
same item is an update and writes nothing.
- **The metadata editor's Delete** (`runReset` with no baseline): an
unbound real delete removes the item from navigation. A reset to a
packaged baseline writes nothing.
- **Publish all** (`usePublishAllDrafts`): before publishing each
package-less page or dashboard draft, it asks the item's layers. The
ones that had none are created and join navigation after the batch. A
failed layers read is not taken for a create.
- **A create no longer duplicates an entry.** `syncPageCreated` /
`syncDashboardCreated` skip an app whose navigation already references
the target at any depth (`navigationReferences`). `addNavigationItem`
itself still appends.
- **`NavigationSyncEffect`** stays exported, `@deprecated`, and renders
nothing. `AppContent` no longer mounts it.
No rename site exists in the console today, so the `sync…RenamedAllApps`
writers keep no caller.
### Who relied on the diff (the claim's pre-code check)
Nothing created outside the console. The diff dropped every platform
artifact, and package installs and CLI deploys carry `_packageId`
(checked on the running showcase: `showcase_start_here`'s layers answer
`_packageId: 'com.example.showcase'`, `_provenance: 'package'`). Studio
always saves with a package. The diff acted only on package-less pages
or dashboards that appeared or vanished in this tab's lists mid-session,
and its baseline re-seeded on every load.
**What no longer reaches navigation:** a package-less page or dashboard
created or deleted outside this tab (a server-side agent, another tab, a
raw REST call). It now reaches navigation only through its own writer.
The changeset says the same.
## Tests
- New `ResourceEditPage.navigationWrites-28680.test.tsx` (5 cases, the
real page): the first publish adds the page to the app once and a
**second publish adds no second entry**; publishing an item that already
had a layer writes nothing; a package-bound publish writes nothing; a
real delete removes the entry; a reset leaves navigation alone.
- New `usePublishAllDrafts.navigationWrites-28680.test.tsx` (2 cases,
real `MetadataClient`): new package-less page and dashboard join; an
updated page, a packaged page and a flow do not; layers are asked before
each publish; a failed layers read writes nothing.
- `useNavigationSync.test.tsx`: the diff's cases are replaced by
`useNavigationWrites` cases (platform artifacts in both directions,
other types, draft preview, locked apps, a second create of a referenced
page or dashboard), `navigationReferences`, and a pin that
`NavigationSyncEffect` reads neither list and writes nothing.
- `labelInherits-9868`, `standardEntryLocalizes-11201` and
`chromeReads.afterFirstPaint-12980` now drive the new door, or pin that
the retired effect reads nothing.
- Ablations, each one red: always-first-publish, publish ignoring the
package binding, no create write, no delete write, delete on reset too,
publish-all without the layers check, publish-all without the write,
writer without dedupe, no preview skip, no platform skip, no type filter
(11 of 11).
## Acceptance notes
Measured on a production console build (`pnpm build`, `pnpm
console:build`, `pnpm dev -- --fresh`), `showcase_app`, admin, API calls
of a warm reload, two runs each with the same count:
| Route | Base `2678600d` | PR 1 (#28754) | This PR | Difference from PR
1 |
|---|---|---|---|---|
| `page/showcase_start_here` | 26 | 26 | **24** | `/meta/page` −1,
`/meta/dashboard` −1 |
| `dashboard/showcase_ops_dashboard` | 35 | 35 | **34** | `/meta/page`
−1 |
| `showcase_task` (object list) | 33 | 33 | **31** | `/meta/page` −1,
`/meta/dashboard` −1 |
On the running server, a page saved as a draft answers `/layers` 404 and
its first publish turns it into a layer with `resettable: false`, which
is the editor's create and delete verdict this PR reads. The probe page
was deleted afterwards.
Deviations:
- **The dashboard route is 34, not the expected 33.** Its own route gate
in `AppContent` waits for the `dashboard` list (`routeReads`), and the
dashboard view reads its definition from it. The effect shared that one
request, so removing the effect saves only `/meta/page` there. Reading
the dashboard route bundle-first, as PR 1 did for the page route, would
remove it. That is `AppContent`'s route gate and the dashboard view,
outside this claim's surface, so it is left for a follow-up card.
- **Publish all from the draft-preview bar writes no navigation.** The
door writes nothing in preview, where `apps` may be draft bodies. The
diff wrote nothing there either.
- `AppContent.tsx` changes only to drop the mount.
`useNavTargetLabel.ts` keeps a comment that says the console's
`NavigationSyncEffect` loads the dashboard list; it is outside the
surface and left as it is.
## Verification
<details><summary>Derived gates (34)</summary>
Run at `f31794f6` (this PR merged with `origin/main` at `cd0ec76b`).
`dispatch-gates --ran`: 34 derived, 34 run, 0 NOT-MEASURED.
```
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
```
</details>
<details><summary>Tests and type-check</summary>
- 295 test files naming the touched modules (`useNavigationSync`,
`ResourceEditPage`, `usePublishAllDrafts`, `AppContent`,
`DraftPreviewBar`, `HomePage`), 5 locked batches at `--maxWorkers=3`:
295 passed, 2493 tests. The full app-shell suite is left to CI's Test UI
shards.
- `pnpm turbo run type-check --filter=@object-ui/app-shell`: 65/65
tasks, exit 0.
- `eslint --quiet` over the changed files: exit 0.
- Merged `origin/main` at `cd0ec76b` with no overlap on touched files
and no parallel fix found.
</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. (run as `eslint --quiet`
over the changed ui files: 0 errors; the whole-workspace lint is left to
CI)
- [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. (not touched)
- [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. (except the 维护者速读
block the dispatch rules require)
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QsR2jUjXYpqdaAkEWBHbqG
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 899782b commit c064d3a
20 files changed
Lines changed: 798 additions & 450 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- chrome
- console
- __tests__
- hooks
- __tests__
- preview
- __tests__
- views/metadata-admin
- previews
- react/src/context
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
14 | | - | |
| 13 | + | |
| 14 | + | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
56 | | - | |
| 56 | + | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
178 | 178 | | |
179 | 179 | | |
180 | 180 | | |
181 | | - | |
| 181 | + | |
| 182 | + | |
182 | 183 | | |
183 | 184 | | |
184 | 185 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
61 | 60 | | |
62 | 61 | | |
63 | 62 | | |
| |||
1452 | 1451 | | |
1453 | 1452 | | |
1454 | 1453 | | |
1455 | | - | |
1456 | 1454 | | |
1457 | 1455 | | |
1458 | 1456 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
73 | | - | |
74 | 73 | | |
75 | 74 | | |
76 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | | - | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
7 | 8 | | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | 9 | | |
12 | 10 | | |
13 | 11 | | |
| |||
39 | 37 | | |
40 | 38 | | |
41 | 39 | | |
42 | | - | |
43 | 40 | | |
44 | 41 | | |
45 | 42 | | |
| |||
73 | 70 | | |
74 | 71 | | |
75 | 72 | | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | 73 | | |
122 | 74 | | |
123 | 75 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
20 | | - | |
21 | | - | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
22 | 24 | | |
23 | 25 | | |
24 | 26 | | |
25 | 27 | | |
26 | 28 | | |
27 | 29 | | |
28 | | - | |
29 | | - | |
| 30 | + | |
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
| |||
64 | 65 | | |
65 | 66 | | |
66 | 67 | | |
67 | | - | |
68 | | - | |
69 | 68 | | |
70 | 69 | | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | 70 | | |
75 | 71 | | |
76 | | - | |
| 72 | + | |
77 | 73 | | |
78 | 74 | | |
79 | | - | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
80 | 88 | | |
81 | | - | |
82 | | - | |
| 89 | + | |
| 90 | + | |
83 | 91 | | |
84 | | - | |
| 92 | + | |
85 | 93 | | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | 94 | | |
91 | 95 | | |
92 | 96 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
21 | | - | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
34 | | - | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| |||
49 | 50 | | |
50 | 51 | | |
51 | 52 | | |
52 | | - | |
53 | | - | |
| 53 | + | |
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
92 | | - | |
93 | | - | |
94 | 92 | | |
95 | 93 | | |
96 | 94 | | |
97 | 95 | | |
98 | 96 | | |
99 | 97 | | |
100 | | - | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
101 | 108 | | |
102 | | - | |
103 | | - | |
| 109 | + | |
| 110 | + | |
104 | 111 | | |
105 | | - | |
| 112 | + | |
106 | 113 | | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | 114 | | |
114 | 115 | | |
115 | 116 | | |
| |||
0 commit comments