Repository navigation
Commit 8196881
feat(runtime): the per-base customizations read names the package behind each field and flow another package contributes (#28749)
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XQB8PvuLXjvzVKRUBkB8969)_
Part of #28722
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: none — the member is on a REST answer that no published
skill teaches, and no metadata author writes it
## 维护者速读
- **改了什么**:每个已安装基础包的「定制读取」(`GET /packages/:id/customizations`) 多返回一张
`contributions`
清单:别的包给这个基础包的对象加的字段,以及别的包挂在这些对象上的流程,每一条都写明是哪个包贡献的。基础包自己的东西和它的 `.custom`
配对包不在里面。
- **为什么改**:客户在 Studio 打开 HotCRM 时,要能看出哪些字段、流程是 HotCRM
自带的,哪些是别的包加进来的(灰显并写明来源,ADR-0029 D7 / ADR-0146
D3)。以前没有任何读取接口说明来源,前端也不许自己猜。
- **风险与回滚**:只加字段,不改已有清单和数字;Studio 现有的「已定制 N 项」计数不受影响。spec 的应答声明是严格的,所以
ui 里手写的几个测试样例补了这个新成员。回滚 = revert 本 PR。导航项(Wanted 3)不在本 PR,等 #27928 的裁决。
- **先看**:① `packages/spec/src/api/package-lifecycle.zod.ts` 新增的
`contributions` 与 `counts.contributions`(契约);②
`packages/runtime/src/domains/packages.ts` 的
`readBaseContributions`(排除基础包、配对包和本环境自己写的流程的三处判断);③
`packages/runtime/src/domains/packages-customizations.test.ts` 新增的
#28722 一组测试。
- **两分钟验证**:`pnpm --filter @objectstack/runtime exec vitest run
src/domains/packages-customizations.test.ts`,预期 17 个测试全绿。
- **需要你做的**:无,席位落地。
## What changes
- **Contract** — `packages/spec/src/api/package-lifecycle.zod.ts`:
`GetPackageCustomizationsResponseSchema` gains `contributions`, an array
of strict entries `{ kind: 'field' | 'flow', object, name, packageId }`,
and `counts.contributions`. Strict like the rest of the answer, and
required: the door always serves it.
- **Where it lives: a member of the existing read, not a new route.**
Measured choice between the N-15 answer and one new read:
- The N-15 read is already the one per-base read Studio fetches for a
base's surface (`packageCustomizations-io.ts`), with the read gate, the
closed query set, the pair-id refusal and the 404 wording this answer
also needs. A new `GET /packages/:id/contributions` would duplicate all
four and add a route-ledger row, a dispatcher mount, a typed client
method and the dogfood route census pins.
- The cost of the member is that hand-built answers in tests must carry
it. Four fixtures do (one runtime mount test, three ui tests); they are
updated here.
- The existing lists keep their meaning: `extensions` stays the pair's
members only, and Studio's "customized" count (overrides + extensions)
is unchanged.
- **Reader** —
`packages/runtime/src/domains/packages.ts#readBaseContributions`, called
by `readPackageCustomizations`. Two sources, each the registry that
already records the contributor:
- **field**: the object registry's `extend` contributors of each object
the base owns (`getObjectOwner`), the same walk the `extensions` list
makes for the pair. Each contributor's `definition` is its extension
body as declared, so its `fields` keys are the fields it adds and its
`packageId` is the package that declared them.
- **flow**: `registry.listItems('flow')` (which already drops a disabled
package's flows), kept when the `start` node's `config.objectName` is a
base-owned object and the body is a code package's
(`isCodeArtifactBody`: not the `sys_metadata` sentinel and not a
tenant-authored overlay). The package is the stamp the registration put
on the flow.
- Excluded: the base and its `.custom` pair (the pair's members stay in
`extensions`). One entry per item and package, so two packages adding a
field of one name are two entries. Ordered by object, kind, name and
package.
- **Hypotheses in the order, measured:**
- `getObjectContributors` already knows the extenders: holds. Read here
exactly as N-15 reads it.
- Stamping the source at fold time (`mergeObjectDefinitions`): not
taken. The fold is shared by every object read and the write door, and
#28565 edits it. Reading the declared `extend` bodies needs no change to
the served object.
- A flow's owning package is on its registry entry: holds (`_packageId`,
stamped by `registerItem`). The trigger object is the `start` node's
`config.objectName`.
- `packages/runtime/src/route-ledger.ts`: the row's note names the new
list (`check-route-ledger-census` green).
- Regenerated: `content/docs/references/api/package-lifecycle.mdx`
(`gen:docs`) and
`docs/audits/2026-07-unknown-key-strictness-ledger.counts/api.md`
(`gen:strictness-ledger`), the two artifacts `check:generated` named
stale, and `packages/spec/authorable-surface/api.json` (the spec build
writes it: one line,
`api/GetPackageCustomizationsResponse:contributions`). No export was
added or removed (`check:api-surface` unchanged).
- `.changeset/28722-customizations-contributions.md`: `minor` on
`@objectstack/spec` and `@objectstack/runtime`. The kind is `added`: the
new member moves the spec's authorable-surface shard
(`packages/spec/authorable-surface/api.json`), and a kind takes at least
`minor` on every package the diff moves (AGENTS.md Post-Task Checklist
step 3). The ui fixture edits are test files, which no package ships.
## Tests
| Test | Head | Base / ablation |
|---|---|---|
| `packages/runtime/src/domains/packages-customizations.test.ts` (17) |
17/17 | `packages.ts` at base `4bf48a8c`: 7 red (the 4 new tests and the
3 whole-answer equalities) |
| ablation: pair exclusion removed | | 4 red |
| ablation: base exclusion removed | | 2 red |
| ablation: `isCodeArtifactBody` guard removed (environment flows
listed) | | 1 red |
| ablation: base-owned trigger object check removed | | 3 red |
The new tests (#28722 describe block) use the card's fixture: a base
with its own flow on its object, the pair extending that object and
carrying a flow, and a third code package registered through
`ObjectQL.registerApp` (the real manifest path) with an
`objectExtensions` field and two flows, one on the base's object and one
on another package's object.
- The read names the third package for its field and its flow, and names
neither the base nor the pair; the answer parses through the declared
schema.
- Two packages adding a field of the same name are two entries, each
with its own package.
- A flow authored in the environment (the `sys_metadata` sentinel, and a
tenant-authored overlay wearing a package id) is not listed.
- Another base's read lists only what is contributed to its own objects.
Each ablation ran through `scripts/ablation-replace.mjs` (anchor hit,
run, restore), and each restore was proven: blob equal to HEAD and `git
diff HEAD` empty. The runtime test imports `packages.ts` from `src`, so
no rebuild was needed between legs.
Suites:
| Suite | Result |
|---|---|
| `pnpm --filter @objectstack/runtime test` | 504 files, 8203 passed, 19
skipped, exit 0 |
| ui `BuilderLanding.customizationCounts-28566`, `customizationMarks`,
`metadataAreaHost.customizations-28568` | 3 files, 22/22, exit 0 |
| `pnpm --filter @objectstack/runtime typecheck`, `pnpm --filter
@objectstack/spec typecheck` | exit 0 |
| `pnpm build` (whole repo) | exit 0 |
## Acceptance notes
- **Wanted 3 (navigation entries)** is not here. It is the same question
as #27928, which waits on the maintainer's ruling, so this PR is `Part
of #28722`.
- **D5, the ui reader.** The served key is pinned by the server test
above. The greyed, attributed drawing belongs to N-19 (#28572), which is
in flight on the ui lane and is not on `main`. Drawing it here would
edit the same Studio surfaces that PR is changing, so the reader is
N-19's (or its follow-up card's) to wire, now that a served source
exists for fields and flows.
- The ui package's own `type-check` needs every `ui/` package built; it
was not run locally. CI's `Type Check UI` covers it.
- Not stamped at fold time, so the folded object read still carries no
per-field source. The contributions list is the served source for that
question.
## Verification
All readings at head `b4a36765` unless named. Head `7edbf44b` changes
only the changeset (kind `added`, `minor`); `check-adr-0087-registration
--base origin/main` and `check-empty-changeset --base origin/main` exit
0 there.
`pnpm --filter @objectstack/runtime exec vitest run
src/domains/packages-customizations.test.ts`: 17/17. With `packages.ts`
restored to base `4bf48a8c`: 7 red.
<details><summary>Derived gates: 109 derived, 109 run, 0 NOT-MEASURED, 0
UNRUN</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`, re-derived at `b4a36765`: the same 109
commands as at `274f09f9`, where the loop ran.
- `--ran` verdict: "109 derived famil(ies) accounted for — 109 run, 0
NOT-MEASURED", exit 0.
- First pass at `274f09f9`: 105 exit 0, and four that did not:
- `check:doc-authoring` exit 1: a real finding. The route-ledger note
carried the tracker id. It is removed in `b4a36765`, and the gate is
green there.
- `check-plugin-teardown-shape --self-test` exit 3: shallow clone. After
fetching its pinned fixture commit, 48 cases pass.
- `check:skill-examples` and `check:dual-build-cjs-loads` exit 3
(PREREQUISITE NOT MET: no built output). After a whole-repo `pnpm build`
(exit 0), both exit 0.
- Re-run at `b4a36765` after the note edit: `check:doc-authoring`,
`check:route-envelope`, `check:issue-citations`, `check:nul-bytes`,
`check:published-files`, `check:type-check-coverage`,
`check:durability-log-level` and the four above, all exit 0.
- `node scripts/check-route-ledger-census.mjs`: exit 0 (115 rows).
- `pnpm --filter @objectstack/spec check:generated --fix`: two artifacts
were stale (docs, strictness ledger). Both were regenerated and
re-checked green.
- eslint `--no-inline-config` over the 5 changed `packages/` TS files:
exit 0. ui eslint over the 3 changed test files: exit 0.
</details>
<details><summary>ui fixtures were needed</summary>
`BuilderLanding.customizationCounts-28566.test.tsx` restored to base: 3
of 6 red against the new schema, because its served answer lacks the
now-required `contributions`. With the fixture carrying it: 6/6 green.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5ogKLgaK3fAZF4kmeF)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d9c4cdc commit 8196881
13 files changed
Lines changed: 257 additions & 19 deletions
File tree
- .changeset
- content/docs/references/api
- docs/audits/2026-07-unknown-key-strictness-ledger.counts
- packages
- runtime/src
- domains
- spec
- authorable-surface
- src/api
- ui/packages/app-shell/src/views
- metadata-admin
- studio-design
| 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 | |
|---|---|---|---|
| |||
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
166 | | - | |
| 166 | + | |
167 | 167 | | |
168 | 168 | | |
169 | 169 | | |
| |||
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
176 | | - | |
| 176 | + | |
| 177 | + | |
177 | 178 | | |
178 | 179 | | |
179 | 180 | | |
| |||
201 | 202 | | |
202 | 203 | | |
203 | 204 | | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
204 | 214 | | |
205 | 215 | | |
206 | 216 | | |
207 | 217 | | |
208 | 218 | | |
209 | 219 | | |
210 | 220 | | |
| 221 | + | |
211 | 222 | | |
212 | 223 | | |
213 | 224 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | | - | |
| 22 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
103 | | - | |
| 103 | + | |
| 104 | + | |
104 | 105 | | |
105 | 106 | | |
106 | 107 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
339 | 339 | | |
340 | 340 | | |
341 | 341 | | |
342 | | - | |
| 342 | + | |
| 343 | + | |
343 | 344 | | |
344 | 345 | | |
345 | 346 | | |
| |||
442 | 443 | | |
443 | 444 | | |
444 | 445 | | |
445 | | - | |
| 446 | + | |
| 447 | + | |
446 | 448 | | |
447 | 449 | | |
448 | 450 | | |
| |||
488 | 490 | | |
489 | 491 | | |
490 | 492 | | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| |||
2527 | 2527 | | |
2528 | 2528 | | |
2529 | 2529 | | |
2530 | | - | |
| 2530 | + | |
| 2531 | + | |
2531 | 2532 | | |
2532 | 2533 | | |
2533 | 2534 | | |
| |||
3093 | 3094 | | |
3094 | 3095 | | |
3095 | 3096 | | |
3096 | | - | |
| 3097 | + | |
3097 | 3098 | | |
3098 | 3099 | | |
3099 | 3100 | | |
| |||
3111 | 3112 | | |
3112 | 3113 | | |
3113 | 3114 | | |
| 3115 | + | |
| 3116 | + | |
| 3117 | + | |
3114 | 3118 | | |
3115 | 3119 | | |
3116 | 3120 | | |
| |||
3168 | 3172 | | |
3169 | 3173 | | |
3170 | 3174 | | |
| 3175 | + | |
| 3176 | + | |
3171 | 3177 | | |
3172 | 3178 | | |
3173 | 3179 | | |
3174 | 3180 | | |
3175 | 3181 | | |
3176 | | - | |
| 3182 | + | |
| 3183 | + | |
| 3184 | + | |
| 3185 | + | |
| 3186 | + | |
| 3187 | + | |
| 3188 | + | |
3177 | 3189 | | |
3178 | 3190 | | |
3179 | 3191 | | |
| 3192 | + | |
| 3193 | + | |
| 3194 | + | |
| 3195 | + | |
| 3196 | + | |
| 3197 | + | |
| 3198 | + | |
| 3199 | + | |
| 3200 | + | |
| 3201 | + | |
| 3202 | + | |
| 3203 | + | |
| 3204 | + | |
| 3205 | + | |
| 3206 | + | |
| 3207 | + | |
| 3208 | + | |
| 3209 | + | |
| 3210 | + | |
| 3211 | + | |
| 3212 | + | |
| 3213 | + | |
| 3214 | + | |
| 3215 | + | |
| 3216 | + | |
| 3217 | + | |
| 3218 | + | |
| 3219 | + | |
| 3220 | + | |
| 3221 | + | |
| 3222 | + | |
| 3223 | + | |
| 3224 | + | |
| 3225 | + | |
| 3226 | + | |
| 3227 | + | |
| 3228 | + | |
| 3229 | + | |
| 3230 | + | |
| 3231 | + | |
| 3232 | + | |
| 3233 | + | |
| 3234 | + | |
| 3235 | + | |
| 3236 | + | |
| 3237 | + | |
| 3238 | + | |
| 3239 | + | |
| 3240 | + | |
| 3241 | + | |
| 3242 | + | |
| 3243 | + | |
| 3244 | + | |
| 3245 | + | |
| 3246 | + | |
| 3247 | + | |
| 3248 | + | |
| 3249 | + | |
| 3250 | + | |
| 3251 | + | |
| 3252 | + | |
| 3253 | + | |
| 3254 | + | |
| 3255 | + | |
| 3256 | + | |
| 3257 | + | |
| 3258 | + | |
| 3259 | + | |
3180 | 3260 | | |
3181 | 3261 | | |
3182 | 3262 | | |
| |||
0 commit comments