Repository navigation
Commit 8d208a4
feat(runtime): the customizations read serves the nav contributions other packages add to a base's apps (#29742)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8KcviDs8Y3wWmPMX818Nmzo)_
Fixes #28722
Ruling-ref: 6566915346
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:`GET /packages/:id/customizations` 的 `contributions` 现在真正返回 `nav`
条目:别的已安装包往本包应用里加的导航项,每条写明是哪个包加的;spec 里 `nav` 臂去掉 [PLANNED — not yet
served] 标记。
- 为什么改:裁决 E(6566915346,「同意」):Studio 要把别的包加进 HotCRM 菜单的条目灰显并标出来源,spec
协议已在 #29401 落地,这是服务端那一半(#28722 第三项)。
- 风险与回滚:只读接口多出 `nav` 条目,形状 spec
早已声明;读的是菜单折叠用的同一个注册表,停用包的条目不列,和菜单一致;导航项本身不加任何键;回滚即撤掉此 PR。
- 先看:`packages/runtime/src/domains/packages.ts`(`readBaseContributions`
新增的 nav
段与排序)、`packages/runtime/src/domains/packages-customizations.test.ts`(新测试)、`packages/spec/src/api/package-lifecycle.zod.ts`(去标记)。
- 两分钟验证:`pnpm --filter @objectstack/runtime exec vitest run
src/domains/packages-customizations.test.ts`,预期 22 个测试全部通过。
- 需要你做的:无,席位落地。
## What changes
The customizations read now serves the `nav` kind of `contributions`
that the spec declared ahead of its producer in #29401 (ruling
6566915346, letter E).
- **`readBaseContributions`**
(`packages/runtime/src/domains/packages.ts`) reads the
navigation-contribution registry (`getAppNavContributions`), the one the
served app's fold (`applyNavContributions`) reads, for every app
registered under the base (`listItems('app', baseId)`). It writes one
entry per item of a contribution's `items` that declares an `id`: `{
kind: 'nav', app, group?, name, packageId }`. `group` is the
contribution's `group` when it names one, including a group the app does
not declare (the fold relocates those items to the top level, and the
spec arm says the group is still named). A separator with no `id` is not
an entry, and a contributed group's children ride inside it.
- **The fold's rules, not a copy of them.** A disabled package's
contribution is skipped with the fold's own predicate
(`isPackageDisabled`, #28117), asked per read. The base and its pair are
excluded through the stamp predicate the `field` and `flow` kinds
already share (`codePackageOf`), which also drops a package-less
registration and the `sys_metadata` sentinel. Nothing is read from or
written to any nav item, and no client-side join on names is involved.
- **Order**, as the member describe declares: the `field` and `flow`
entries first, ordered as before. Then the `nav` entries, by app, group
(no group first), name and package. `counts.contributions` counts every
entry.
- The forced `Exclude` type annotation #29401 added is gone. The local
types are the member's own arms.
- **Spec** (`packages/spec/src/api/package-lifecycle.zod.ts`): the `nav`
arm's describe drops `[PLANNED — not yet served]`, and so do the member
describe, the schema describe, the file header and the schema docblock.
The docblock keeps the ruling's MUSTs: the same registry, no disabled
package, no key on any nav item. The schema accepts exactly what it
accepted before. The planned-marker pin in `package-lifecycle.test.ts`
is flipped in the same change: the arm, the member and the schema now
carry no `PLANNED` / `not yet served`.
- Regenerated `content/docs/references/api/package-lifecycle.mdx`
(`gen:docs`, the only artifact `check:generated` named stale).
`content/docs/api/metadata-api.mdx` now names the `nav` kind as served.
- Changeset
`.changeset/28722-customizations-serve-nav-contributions.md`:
`@objectstack/runtime` patch and `@objectstack/spec` patch (the describe
text ships in `dist`). The authorable-surface shard did not move:
`check:generated`, including `check:authorable-surface`, exits 0 with no
`packages/spec/authorable-surface/**` change. So the declaration is
`Metadata-protocol: none`, and `Clause-②: no` because the arm was
already declared.
## Why
#28722 Wanted 3: Studio needs one served answer naming the package
behind each navigation entry another package adds to an installed base's
app, so it can draw those entries greyed with their source (ADR-0029 D7,
ADR-0146 D3, #27356 row 20). The spec slice (#29401) declared the shape.
This serves it from the registry the menu fold reads.
## Tests
`packages/runtime/src/domains/packages-customizations.test.ts`. The
fixture's base now ships an app with a group, and the other base ships
one of its own. The card's third package also contributes a nav item
through its manifest `navigationContributions`. The pair contributes a
nav item to the base's app, and so does the base itself.
- The card's test, updated: the third package is named for its field,
its flow **and its nav item**, and neither the base's nor the pair's nav
item is listed (count 3). The environment-flow test's expected list
gains the nav entry.
- New: one entry per item with an id, with its group (including an
undeclared group), no-group entries first, field and flow entries before
nav. A separator is not an entry, a contributed group's child is not an
entry, and the answer parses through
`GetPackageCustomizationsResponseSchema`.
- New: a disabled package's items are not listed and re-enabling
restores them. AGREEMENT: at each step, the listed names equal the ids
the served app (`registry.getApp`) carries beyond the base's and the
pair's own.
- New: a contribution to an app the base does not own is not the base's.
Another base's read lists the one made to its app, and one made to an
app no package declares is nobody's.
- New: a package-less contribution and one stamped with the
`sys_metadata` sentinel are not listed.
- `packages/spec/src/api/package-lifecycle.test.ts`: the three
planned-marker pins flipped.
<details><summary>Red first, then green</summary>
- Test commit `8b347c02` on base `744211fc` (`packages.ts` unchanged):
`vitest run src/domains/packages-customizations.test.ts` exit 1, 6
failed and 16 passed of 22. Every failure is an AssertionError on
missing `nav` entries.
- Spec pin flip against the unchanged `package-lifecycle.zod.ts`: exit
1, 3 failed and 50 passed of 53. With the zod edit: exit 0, 53 of 53.
- Implementation `66293723`: runtime suite file exit 0, 22 of 22.
</details>
<details><summary>Ablations (each through scripts/ablation-replace.mjs,
restored to the HEAD blob 21dfef8, git diff HEAD empty; runtime
reads packages.ts from src, so no rebuild between legs)</summary>
- The disabled-package skip deleted: 1 red (the disabled test).
- `codePackageOf` replaced by the raw `packageId`: 6 red (base, pair,
sentinel and package-less items appear).
- `listItems('app', baseId)` replaced by `listItems('app')`: 2 red (the
not-owned test, and another base's read).
- The item-id guard deleted: 1 red (the separator becomes an entry).
- The `group` spread deleted: 2 red. The first attempt passed an empty
`--replacement`, which the tool refused before running anything
(replacement count 0 to 0). It was rerun with `--delete`.
</details>
<details><summary>Local readings (final head `58810e41`)</summary>
- Merged `origin/main` twice through `scripts/pm/os-regen-merge.sh`: at
`fc558923`, and at `3d5933a9` after #29651 touched both
`package-lifecycle.zod.ts` and `packages.ts` (no conflicts). The second
merge took main's side of
`content/docs/references/api/package-lifecycle.mdx`, which was then
regenerated from the merged tree (`gen:schema` via the spec build, then
`gen:docs`) in `a4c5be41`. A third merge at `0eb97fa4` (`58810e41`) took
in #29687, which is #29649's change to `packages.ts` (seat 4's serial
PR). It merged with no conflicts and no regeneration owed. Every reading
below was retaken at `58810e41` unless it names another head.
- Builds, under os-verify-lock: `turbo run build
--filter='@objectstack/runtime...'` VERDICT command-exit 0 (34 tasks) at
`58810e41`. `turbo run build --filter='./packages/**'` VERDICT 0 (78
tasks) was the prerequisite for `check:skill-examples` and
`check:dual-build-cjs-loads`, which exit 3 with no built output.
- `packages/spec` `check:generated`: before regeneration it named
exactly `check:docs` stale. After `gen:docs` it exits 0, including
`check:authorable-surface` with no shard change, which is the
`Metadata-protocol: none` reading. One run at `a4c5be41` reported
`check:test-typecheck` as `tsc … failed without diagnostics` when
started without `NODE_OPTIONS`. Rerun alone with
`--max-old-space-size=4096` it is OK (52 files, 240 errors, 131 pinned
signatures held), and the final `check:generated` exits 0.
- `pnpm --filter @objectstack/spec test`: exit 0, Test Files 803 passed
(803), Tests 23782 passed, 1 todo (head `9540d333`).
`src/api/package-lifecycle.test.ts` at `58810e41`: 57 passed.
- `pnpm --filter @objectstack/spec typecheck`: exit 0.
- `pnpm --filter @objectstack/runtime typecheck`: exit 0 at `58810e41`.
The first run at `9540d333` exited 1 only because
`@objectstack/driver-turso` had no `dist` (TS2307 in an untouched test
file). Once that package was built, it exits 0.
- `pnpm --filter @objectstack/runtime test` under the lock, at
`58810e41`: VERDICT command-exit 0, Test Files 531 passed (531), Tests
8529 passed, 19 skipped.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `58810e41` (and before at `a4c5be41`,
same result): 104 commands, all 104 exit 0 (stdin closed, exit captured
before any pipe). `--ran`: 104 derived, 104 run, 0 NOT-MEASURED, 0
UNRUN, exit 0. In the first round at `9540d333`, three commands exited 3
on prerequisites: `check-plugin-teardown-shape --self-test` (shallow
clone; its fixture commit was fetched, then 48 cases pass), and
`check:skill-examples` and `check:dual-build-cjs-loads` (no built
output; exit 0 after the build above).
- `pnpm lint`: exit 0 at `58810e41`.
- Not run locally: `test:repo` (CI's Test Core), and full dogfood (no
`!` in the title, no narrowing arm).
</details>
## Acceptance notes
- Serial: #29342 (seat 5) edits the same file header paragraph of
`package-lifecycle.zod.ts` that lists the declared-ahead members. This
PR leaves only `withheldMaps` there, so whichever lands second takes the
other's sentence. #29649 (seat 4) touches `ownDocumentsOf` in
`packages.ts`, a region disjoint from this one. #29342 had not landed at
the final merge. #29649 landed as #29687 and is merged in (`58810e41`).
- Observation, not filed: for a disabled base, `listItems('app',
baseId)` already hides the base's app, so the read lists no `nav` entry,
while `field` entries on its objects are still listed. Nothing is served
to contribute to in that state, and the docblock says so.
## Verification
`pnpm --filter @objectstack/runtime exec vitest run
src/domains/packages-customizations.test.ts` → 22 passed.
## 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 by [Claude
Code](https://claude.ai/code/session_01JQwxbYJ6AF6nA1jMoub4rQ)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01JQwxbYJ6AF6nA1jMoub4rQ
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 4ab2e7e commit 8d208a4
7 files changed
Lines changed: 240 additions & 67 deletions
File tree
- .changeset
- content/docs
- api
- references/api
- packages
- runtime/src/domains
- spec/src/api
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
113 | | - | |
114 | | - | |
115 | | - | |
| 113 | + | |
| 114 | + | |
116 | 115 | | |
117 | 116 | | |
118 | 117 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
50 | 49 | | |
51 | | - | |
| 50 | + | |
| 51 | + | |
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| |||
170 | 170 | | |
171 | 171 | | |
172 | 172 | | |
173 | | - | |
| 173 | + | |
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
| |||
180 | 180 | | |
181 | 181 | | |
182 | 182 | | |
183 | | - | |
| 183 | + | |
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
| |||
222 | 222 | | |
223 | 223 | | |
224 | 224 | | |
225 | | - | |
| 225 | + | |
226 | 226 | | |
227 | 227 | | |
228 | 228 | | |
| |||
0 commit comments