Repository navigation
Commit e9dec3d
Fixes #21442
Clause-②: no
## What this changes
`GET /api/v1/meta/view?object=OBJECT` (the object door, `getMetaItems`)
lists the views a stored view container expands. The by-name read (`GET
/api/v1/meta/view/NAME`, `getMetaItem`) expanded no container. It
answered such a name only on an unscoped kernel and only for an
environment-wide container, where registry hydration had registered a
copy. This PR implements triage's ruling A (comment 5957375321) as
ruled: the by-name read expands the in-scope stored containers through
`expandRuntimeViewContainer`, the function the list read uses. Nothing
is persisted or registered, and there is no kernel-specific branch.
- **By-name read (`getMetaItem`).** A new step 1b sits after the
stored-row read and before the MetadataService and registry steps. When
a view name has no stored row of its own and a stored view container in
the caller's scope expands it, the read answers the item the list read
serves under that name. The step sits before the MetadataService and
registry steps because the list read's expansion also wins over items of
the same name from those two sources.
- **One selection and one expansion, shared with the list read.** Three
parts of `readFlattenedMetaItems` move unchanged into private methods:
its row selection (the two `queryByOrg` reads, the overlay cache and the
org-over-env merge), its row parse, and its expansion loop. The new
methods are `readActiveOverlayRows`, `storedOverlayEntries` and
`expandStoredViewContainers`, and both doors call them.
`resolveRowlessExpandedView` is the by-name use of the three.
`expandRuntimeViewContainer` is called as is and is not edited. The list
read's own behaviour is unchanged, and the package's full suite still
passes over it.
- **Layers (`getMetaItemLayered`).** For such a name the layers report
the container's provenance in fields that already exist. `overlay` is
the container's own stored row: its `name` is the container's, and its
`_packageId` is the package the row is bound to. `overlayScope` is the
scope that row was read from (`env` or `org`), and `effective` is the
expanded view, which is what the by-name read answers. `code` keeps its
own read: the item a package ships under the name, else `null`.
- **History and diff (`historyMetaItem`, `diffMetaItem`).** They resolve
to the container's own row. Each answers exactly what it answers under
the container's row name for the same caller, and says so: every event's
`ref.name` is the container's, and the diff's `name` is the container's
(the item actually diffed, the same rule its echoed `type` already
follows). No history is synthesized for a name that was never stored.
- **Control.** The container's own name still answers its stored row on
every read.
## Measured, in-process at the protocol (the #21334 showcase harness:
`showcase_task`, every member kind)
| | before, `env_local` | before, unscoped | after, both kernels |
|---|---|---|---|
| package-scoped container (saved into another writable package) |
nothing | same item as the list | same item as the list |
| package-less, environment-wide container | nothing | same item as the
list | same item as the list |
| package-less, organization-scoped container | nothing | nothing | same
item as the list |
| tenant overlay of the package's own container, `showcase_task.default`
| the packaged view (`All Tasks`) | the overlay's view (env-wide) / the
packaged view (org-scoped) | the overlay's view |
| layers for `showcase_task.os_qa_probe.in_progress` (package-scoped
container) | `overlay: null`, `effective: null` | `overlay: null`,
`code` and `effective` = the hydrated copy | `overlay` = container
`os_qa_probe`, `overlayScope` = its scope, `effective` = the item |
| history for that name (package-scoped container) | `[]` | `[]` | the
container's own events, `ref.name: os_qa_probe` (every scope) |
| diff for that name (package-scoped container) | empty, `name` echoed |
empty, `name` echoed | the container's own diff, `name: os_qa_probe`
(every scope) |
"Before" is `origin/main` at `5555047117`. "After" is this branch. Both
were run with the same harness, and the item comparison excludes
`_diagnostics`.
## Provenance needed no new field (PM mechanism assumption 3)
The layered response already carries `overlay` (the stored row behind
the name) and `overlayScope` (its scope). History events already carry
`ref.name` and `ref.org`, and the diff already echoes `name`. So no
published response shape gains or loses a key, and `Clause-②: no` stands
as claimed.
## A write by an expanded name (out of scope, recorded, unchanged)
Probe: `saveMetaItem` with type `view`, the name
`showcase_task.os_qa_probe.in_progress` and a ViewItem body. On both
kernels and for all three container scopes, the save door ACCEPTS it and
stores a row under that name. Afterwards the by-name read answers the
written row, and the object door still lists the container's expansion
for that name. The probe gave byte-identical results with `protocol.ts`
at `5555047117` and at this branch: `saveMetaItem` is not in this diff,
and it does not call any read this PR changes for a view. The
disagreement it leaves is filed as a finding in the dev report (the list
read's upsert by name displaces a stored row of the same name), not
fixed here.
## Reverse verification (each mutation committed-state, landed through
`scripts/ablation-replace.mjs`, restore proven blob == HEAD and `git
diff HEAD` empty)
The subject resolves through relative source imports (`./index.js`), so
no `dist/` leg applies.
- **A1, by-name step 1b off** (`if (expanded !== undefined && false)`):
`view-container-runtime-expansion.test.ts` went from 119/119 to **30
failed / 89 passed**. The failures were every `env_local` case (21); on
the unscoped kernel, the organization-scoped cases, the isolation case
and the org-scoped overlay case (8); and the cross-kernel case (1). The
unscoped environment-wide cases stayed green, which is the pre-fix
registry-hydration answer the card describes.
- **A2, layers resolution off**: **10 failed** (every layers assertion,
both kernels).
- **A3, history delegation off**: **6 failed** (the history assertions,
both kernels).
- **A4, diff delegation off**: **6 failed** (the diff assertions, both
kernels).
## Tests
- New, in
`packages/metadata-protocol/src/view-container-runtime-expansion.test.ts`,
nested in the #21334 block so the faithful-registry harness is reused:
49 cases. They cover both kernels × all three container scopes × every
member kind: every name the object door lists (the packaged names
included) answers that same item by name, and the container's own name
is the control. Further cases cover the layers, history and diff per
scope; that the reads store no row and register nothing; that a name
nothing expands answers nothing and gets no history; organization
isolation; a tenant overlay of the package's own container (env-wide and
org-scoped); and a cross-kernel item equality.
- `get-meta-item-org-read-gate.test.ts` /
`get-meta-item-layered-org-read-gate.test.ts`: their engine double
declared only `findOne`, and a row-less view name now also issues `find`
for the container selection. The double gains a `find` that records
partitions. The `view` case now also asserts that the selection reads
the same partitions (`[null, ORG]`). The `find` is assigned onto the
existing double rather than declared in its literal, so the double's
`check:engine-double-contract` accounting is unchanged and no ledger
moves.
- At the final head `3558ad6e70`: `pnpm --filter
@objectstack/metadata-protocol typecheck` clean (`tsc --listFiles`
includes all three edited test files). The full suite (`vitest run`)
gave 205 files passed / 3 skipped, **3151 tests passed** / 19 skipped.
- Downstream, against the rebuilt `dist/` (a narrowed, declared sample,
chosen as the test files that read a view by name through the real
protocol): `@objectstack/objectql` 10 files / 226 tests,
`@objectstack/rest` 9 files / 123 tests, `@objectstack/runtime` 3 files
/ 43 tests, all green. The rest of those packages is CI's.
- Gates: `dispatch-gates` derived 64 families for this tree
(`3558ad6e70`). 63 ran green after the final commit. 1 is NOT MEASURED:
`check:dual-build-cjs-loads` exit 3 PREREQUISITE NOT MET, because it
reads every package's `dist/` and about 38 packages were not built
locally. `--ran` reconciliation: 64 accounted, 63 run, 1 NOT-MEASURED, 0
UNRUN.
- Lint, narrowed: `eslint --no-inline-config --format json` over the 4
changed TypeScript files gave 4 files, 0 errors, 0 warnings. The config
does no type-aware linting (no `parserOptions.project`; see the note at
`eslint.config.mjs` line 328), so this diff cannot move a verdict on an
untouched file. A repo-wide `pnpm lint` is CI's.
## Acceptance notes
- **Cost.** A by-name read of a view name with no stored row of its own
(every packaged view, for instance) now also reads the container
selection. That is the list read's two `find`s, served from the same
overlay cache entry the object door uses (same key: type, package, gated
organization) on an engine with a write epoch. It is the price of one
expansion rule rather than two.
- **Claim surface, declared.** Four edits fall outside `getMetaItem`,
history and diff. The three-method extraction in
`readFlattenedMetaItems` is a pure move, needed so that the by-name read
reuses the list's selection rather than a second copy (PM mechanism
assumption 2). The `getMetaItemLayered` edit is the ruling's Layers
bullet. The two read-gate test doubles are described above. No edit
touches the save door, `deletePackage` or `packages/rest`.
- **Kernel-dependent residue this PR does not change.** The item every
door answers is now the same on both kernels. On an unscoped kernel, the
registry still holds the write-through-hydrated copies of an
environment-wide container's expansions, without the tenant marker the
container itself is given. A package-bound container's copies therefore
pass for code artifacts there. So `resettable` reads `true` on the
by-name envelope, the layers' `code` is the hydrated copy (on
`env_local`: `false`, `null`), and `isArtifactBacked` is true for a
write by that name. The producer-side fix would change the save door's
intent for a write by an expanded name, which the ruling keeps
unchanged, so it is reported as a finding and not fixed here.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DDZNkDVwPQnevTFcYE47H3)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2df621a commit e9dec3d
5 files changed
Lines changed: 594 additions & 167 deletions
File tree
- .changeset
- packages/metadata-protocol/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
104 | 107 | | |
105 | 108 | | |
106 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
107 | 128 | | |
108 | 129 | | |
109 | 130 | | |
110 | 131 | | |
111 | 132 | | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
| 133 | + | |
127 | 134 | | |
128 | 135 | | |
129 | 136 | | |
| |||
137 | 144 | | |
138 | 145 | | |
139 | 146 | | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
140 | 153 | | |
141 | | - | |
| 154 | + | |
142 | 155 | | |
143 | 156 | | |
144 | 157 | | |
| |||
443 | 456 | | |
444 | 457 | | |
445 | 458 | | |
446 | | - | |
| 459 | + | |
447 | 460 | | |
448 | 461 | | |
449 | 462 | | |
450 | 463 | | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
451 | 467 | | |
452 | 468 | | |
453 | 469 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
116 | 119 | | |
117 | 120 | | |
118 | 121 | | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
119 | 140 | | |
120 | 141 | | |
121 | 142 | | |
122 | 143 | | |
123 | 144 | | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
| 145 | + | |
139 | 146 | | |
140 | 147 | | |
141 | 148 | | |
| |||
149 | 156 | | |
150 | 157 | | |
151 | 158 | | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
152 | 165 | | |
153 | | - | |
| 166 | + | |
154 | 167 | | |
155 | 168 | | |
156 | 169 | | |
| |||
389 | 402 | | |
390 | 403 | | |
391 | 404 | | |
392 | | - | |
| 405 | + | |
393 | 406 | | |
394 | 407 | | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
395 | 411 | | |
396 | 412 | | |
397 | 413 | | |
| |||
0 commit comments