Repository navigation
Commit fd43dc2
fix(metadata-protocol): a .custom pair's published override of a page, app, dataset, doc or book is served from its row by every read (#29707)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8XgtQ7Gh7PURXHo26gxhWum)_
Fixes #29613
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:`.custom`
配对包对基础包页面、应用、数据集、文档、文档书的已发布覆盖,所有读取都从覆盖自己的存储行提供,和仪表盘、报表一样;注册表裸槽位不再放一份穿着基础包信封的副本。`/me/apps`
改读协议的有效列表。
-
为什么改:这五种类型不接受环境覆盖,读取把配对包的覆盖行也当成被封存的覆盖拒掉,覆盖只能靠裸槽位副本提供。真实启动(有元数据服务)时按名读取返回基础包原件,列表却返回覆盖,分层读取和配对包的按包层从来看不到覆盖,违背
ADR-0146 D8。
- 风险与回滚:按名读取(真实启动)、分层读取的 effective、按包层会变化,全部朝 D8
方向,席位已批准;已经返回覆盖的读取不变。回滚即还原本 PR,无需迁移,重启即按新规则重新水合。
- 先看:`packages/metadata-protocol/src/protocol-lock-state.ts` 的
`declinesStoredRow`;`protocol-package-bindings.ts`
的两个类型清单;`plugin-hono-server` 的 `servedAppsOf`。
- 两分钟验证:`pnpm --filter @objectstack/objectql exec vitest run
src/protocol-custom-override-bare-slot-envelope.test.ts`,预期 24
个用例全绿(其中五种类型各两种启动形态、重启前后)。
- 需要你做的:无,席位落地
## What changes
A `.custom` pair's published override of a base's `page`, `app`,
`dataset`, `doc` or `book` is now served from its stored row by every
read, the way a dashboard's or report's already is. The registry's bare
slot no longer holds a copy of it wearing the base's envelope.
These five types are sealed (ADR-0131 D6: `allowEnvironmentOverlay:
false`). Every read asked `declinesStoredRow`, which declined their
stored rows as overlays of managed content (`isUnservedSealedOverlay`),
and that included the pair's declared override. The only place the
override was served from was the bare slot. With a metadata service
(every real boot), the by-name read answered the service's copy of the
base's body before it reached the registry.
- `packages/metadata-protocol/src/protocol-lock-state.ts`:
`declinesStoredRow` admits, for these five types, the shipping package's
declared override, judged by `isServedOverrideRowOf` on the row's own
`package_id` and body against `packagedArtifactOwner`. This is the same
admission flow got in #28412, and the same predicate the boot's
sealed-overlay census already uses to leave the override out of its
warning. Every other sealed row of the name stays declined. Every reader
already passes the row it serves (by-name, both lists, layered, the
per-package customizations read), so no reader is edited.
- `packages/metadata-protocol/src/protocol-package-bindings.ts`: the
closed list `SEALED_ROW_SERVED_OVERRIDE_TYPES` and
`servesSealedOverrideRow`; the five types join
`BORROWED_ENVELOPE_DECLINED_TYPES`, so the #22057 guard keeps their row
out of the bare slot under #29600's condition (the row's package ships
no artifact of the name while another package does). The #4624
two-shipper case keeps its bare entry. `mapping` (overridable and
sealed) is left out: no card measured its reads. `action` and `flow`
wait for ADR-0146 D9.
- `packages/metadata-protocol/src/protocol.ts`: comments only. Three
docblock sentences that said these five types still register in the bare
slot are corrected. No code line changes.
- `packages/plugins/plugin-hono-server/src/current-user-endpoints.ts`:
`GET /me/apps` lists the protocol's served apps (`getMetaItems({ type:
'app' })`, what `GET /meta/app` serves) when a protocol service with
that read is present. With no protocol service, today's registry path is
unchanged. A protocol list read that throws is warned once and answered
from the registry. File surface confirmed with the seat first.
- `.changeset/29613-sealed-pair-override-row-served.md`: patch for both
packages.
## Why
ADR-0146 D8 item 2: every read that serves a base's name serves the
`.custom` item that declares it. Item 3: the pair's per-package layer
carries the overriding item, and the base's carries the base unchanged.
Measured on main `744211fc` (table below), these five types broke both
items, and on a real boot the by-name read and the list disagreed about
the same name.
The card's two hypotheses reconcile: the #29600 harness has no metadata
service, so its by-name read fell to the bare slot (override); the
#29649 real-boot reading has one, so its by-name read answered the
shipped body. Both readings are reproduced below, one harness run each
way.
## Tests
**Before and after, per type.** Identical for `page`, `app`, `dataset`,
`doc` and `book`. S = the shipped body, O = the pair's published
override. Before = main `744211fc`, the #29600 harness, without and with
a metadata service holding the code package's bodies. After = this
branch, both shapes, and after a restart.
| Reader | Before, no metadata service | Before, with one (a real boot)
| After (both, and after a restart) | ADR-0146 D8 |
|---|---|---|---|---|
| `getMetaItem` (no package, BASE, PAIR) | O | **S** | O | item 2 |
| `getMetaItems` (no package, BASE) | [O] | [O] | [O] | item 2
(unchanged) |
| `getMetaItemLayered(BASE).effective` | **S** | **S** | O | item 2 |
| per-package layer, PAIR | **[]** | **[]** | [O] | item 3 |
| per-package layer, BASE | **[S, O]** | **[O]** | [S] | item 3 |
| registry bare slot | O wearing BASE | O wearing BASE | none | |
| `registry.getItem` (bare, BASE, PAIR) | O | O | S | registry-direct |
| `registry.getAllApps` (app) | [S, O] | [S, O] | [S] | registry-direct
|
| `GET /me/apps` (app) | the registry's list | the registry's list | the
protocol's served list: O | item 2 |
Each moved reading is seat-approved as code aligning to D8 (no protocol
change, so `Clause-②: no` and `Metadata-protocol: none`).
New and extended pins:
-
`packages/objectql/src/protocol-custom-override-bare-slot-envelope.test.ts`:
a second top-level describe in #29600's shared harness file (the rule on
main since #29633). Per type, with and without a metadata service, every
reader in the table at once and after a restart over the same stores (10
tests). CONTROL per type: a package-less environment row of the base's
name (pre-seal hatch residue) stays unserved: by-name and layered answer
S (5 tests). The driver now takes optional shared stores and a
module-level id counter so a restart works; the #29600 tests are
untouched otherwise. 24/24 green.
-
`packages/metadata-protocol/src/protocol-package-bindings.sealed-override.test.ts`
(new; the module had no test file): the two closed lists agree, plural
spellings fold, `mapping`/`action`/`flow`/regime-O types are outside,
the #4624 case keeps its bare entry. 20/20 green.
-
`packages/plugins/plugin-hono-server/src/current-user-endpoints-delegated-resolution.test.ts`:
a `[#29613]` describe beside the file's other `/me/apps` pins. With a
protocol service the served list is the source and the registry is not
read; with none, or with a protocol that has no list read, the registry
as before; a failing list read is warned and answered from the registry.
14/14 green.
<details><summary>Ablation (both mechanism lines back to main's
behaviour)</summary>
The fix was committed first. `servesSealedOverrideRow` was mutated to
return false (marker `ABLATION29613`) and the five types were removed
from `BORROWED_ENVELOPE_DECLINED_TYPES` (marker `ABLATION29613B`), `grep
-c` = 2 on disk, then `pnpm --filter @objectstack/metadata-protocol
build`.
```
objectql protocol-custom-override-sealed-row-served (now folded into the shared file):
× page/app/dataset/doc/book, with no metadata service: every reader serves the override, at once and after a restart
× page/app/dataset/doc/book, with a metadata service (a real boot): every reader serves the override, at once and after a restart
Tests 10 failed | 5 passed (15) <- the 5 CONTROL cases stay green, by design
metadata-protocol protocol-package-bindings.sealed-override:
Tests 6 failed | 14 passed (20)
restore: git checkout HEAD -- protocol-package-bindings.ts; DIFF-HEAD-EMPTY=yes; rebuilt;
ablation-dist-preflight --absent: tree clean against HEAD, ABSENT-EXIT=0
```
The preflight's presence leg printed a refusal about an untracked path
at that moment instead of a marker count. The 10 objectql reds read the
protocol from `dist/`, which is the evidence the mutation reached it.
`/me/apps` ablation (`ablation-replace.mjs`, `servedAppsOf(ctx)`
replaced by `undefined`, source-imported): 2 failed | 2 passed (the two
served-list cases red; the two registry cases green by design);
restored, blob equals HEAD.
</details>
<details><summary>Package suites (on the branch before the merge of
main)</summary>
```
@objectstack/objectql test Test Files 544 passed (544) Tests 10616 passed (10616) exit 0
@objectstack/metadata-protocol test Test Files 309 passed | 4 skipped (313) Tests 22282 passed | 26 skipped exit 0
@objectstack/plugin-hono-server test Test Files 39 passed (39) Tests 476 passed (476) exit 0
```
After the merge of main (closure rebuilt): runtime
`packages-export-base-unchanged-per-type.integration.test.ts`,
`packages-customizations.test.ts`,
`packages-ensure-customization.test.ts`: 56/56, exit 0. `typecheck` for
metadata-protocol, objectql, plugin-hono-server: exit 0.
</details>
#29649's showcase-boot export pin: `pairServed` widened to all eight
types (`f40dd0e68`); 8/8 green, ablation 5 red.
## Acceptance notes
- Two shippers of one of these names: the admission judges the row
against `packagedArtifactOwner` (the first package the artifact lookup
answers), as flow's admission does. A pair override of the OTHER shipper
would stay declined, and with the guard its bare entry goes too. Not
measured; no shipped stack has two packages shipping one page, app,
dataset, doc or book name.
- `mapping` is overridable and sealed and has the same shape. Left out
on purpose (no measurement).
- #29649 landed first (`33ee9aa1`); its pin re-run on the merged head,
green, and widened.
- The base export (`packages/runtime/src/domains/packages.ts`) is not
touched. With this PR the base's per-package layer lists S alone for
these types, which is what #29649's filter assumes.
## Verification
Local subset on head `90267e800` (after the merge of main), derived with
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (8 paths), each exit captured before any
pipe, then reconciled:
```
✓ dispatch-gates --ran: 65 derived famil(ies) accounted for — 64 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3).
```
- NOT MEASURED: `pnpm check:dual-build-cjs-loads`, reason: PREREQUISITE
NOT MET (it reads every package's `dist/`, and only this change's
closure is built here). CI's `Lint & Repo Gates` runs it.
- `node scripts/check-plugin-teardown-shape.mjs --self-test` first
exited 3 (its pinned fixture commit was outside the shallow clone);
after `git fetch origin 621a487` it exits 0.
- `pnpm lint`: exit 0.
<details><summary>Recorded exit codes</summary>
```
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-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: 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-engine-split-ratio.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-tenant-audit-census.mjs :: exit 0
node scripts/check-tenant-audit-census.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:authz-resolver :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: 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:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 3
pnpm check:durability-log-level :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:error-status-conformance :: exit 0
pnpm check:filter-alias-parity :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-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:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:route-envelope :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: 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
pnpm lint :: exit 0
```
</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. (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.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbXCAvuF6biLpPaEcJ5)_
<!-- signed: domain:server#4 · os-warren ·
session_01JQwxbacSXJQqAztgt4UMrS -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 10e4a77 commit fd43dc2
9 files changed
Lines changed: 476 additions & 32 deletions
File tree
- .changeset
- packages
- metadata-protocol/src
- objectql/src
- plugins/plugin-hono-server/src
- runtime/src/domains
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
| 53 | + | |
53 | 54 | | |
54 | 55 | | |
55 | 56 | | |
| |||
147 | 148 | | |
148 | 149 | | |
149 | 150 | | |
150 | | - | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
151 | 160 | | |
152 | 161 | | |
153 | 162 | | |
| |||
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
306 | 306 | | |
307 | 307 | | |
308 | 308 | | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
314 | 313 | | |
315 | 314 | | |
316 | | - | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
317 | 356 | | |
318 | 357 | | |
319 | 358 | | |
| |||
462 | 501 | | |
463 | 502 | | |
464 | 503 | | |
| 504 | + | |
465 | 505 | | |
466 | 506 | | |
467 | 507 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8764 | 8764 | | |
8765 | 8765 | | |
8766 | 8766 | | |
8767 | | - | |
| 8767 | + | |
| 8768 | + | |
| 8769 | + | |
| 8770 | + | |
8768 | 8771 | | |
8769 | 8772 | | |
8770 | 8773 | | |
| |||
10851 | 10854 | | |
10852 | 10855 | | |
10853 | 10856 | | |
10854 | | - | |
10855 | | - | |
10856 | | - | |
10857 | | - | |
10858 | | - | |
| 10857 | + | |
| 10858 | + | |
| 10859 | + | |
| 10860 | + | |
| 10861 | + | |
10859 | 10862 | | |
10860 | 10863 | | |
10861 | 10864 | | |
| |||
11015 | 11018 | | |
11016 | 11019 | | |
11017 | 11020 | | |
11018 | | - | |
| 11021 | + | |
11019 | 11022 | | |
11020 | 11023 | | |
11021 | 11024 | | |
| |||
0 commit comments