Repository navigation
Commit fbcbcf1
Fixes #22188
Clause-②: yes
## What this changes
`?package=all` is the metadata list's "show everything" scope. #22128
(PR #22185) gave the item read, the save and the publish one reading of
`?package=`: `metaItemPackageBinding`, under which `all`, the empty
value and a non-string name no package. The other `/meta` doors
forwarded the raw value, so `all` reached the store as a package id that
no item is bound to. Every door that reads `?package=`, on both hosts,
now reads it through that one function.
- **`RestServer`** (`packages/rest/src/rest-server.ts`): the layered
read (`serveMetaItemLayered`), the list (`GET /meta/:type`), the book
tree (`GET /meta/book/:name/tree`) and the diagnostics sweep (`GET
/meta/diagnostics`, see H3).
- **The runtime dispatcher** (`packages/runtime/src/domains/meta.ts`,
the `/meta` domain behind the `@objectstack/hono` `${prefix}/*`
catch-all): its book tree, layered read, item branch (the read, the
`?state=draft` read and the `PUT`) and list.
- **Widened public surface (`Clause-②: yes`).** `@objectstack/rest`'s
package entry now exports `metaItemPackageBinding`, and the runtime
imports it beside `metaSaveRequestOptions`. That is the only new export.
Changesets: `@objectstack/rest` `minor` with `Clause-②: yes (widening)`,
and `@objectstack/runtime` `patch` with `Clause-②: no`.
- Unchanged: a real package id still scopes each read and still binds a
save. The item read's cache bypass (`packageScoped`, H3) still reads the
raw parameter.
## Readings (H1 to H5, on this branch's base `ffb31fca`)
**H1, reproduced on the real stack, each door.** The pin file below was
run against the base sources before the fix. Each door has its own
better-sqlite3 `:memory:` store with the real `sys_metadata*` objects
and a real `ObjectStackProtocolImplementation`. The store is seeded
through the protocol: `case_grid` in `com.probe.pkg`, `other_grid` in
`com.other.pkg`, the env-local `local_grid`, and the book `probe_book`
with the page `probe_page` in `com.probe.pkg`.
| door | request | base | plain-read control (no `?package=`) | this
branch |
|:--|:--|:--|:--|:--|
| `RestServer` | `GET /meta/view/case_grid/layers?package=all` | **404
`RESOURCE_NOT_FOUND`** | 200, overlay `live` | as the control |
| `RestServer` | `GET /meta/view?package=all` | **`[]`** | `case_grid`,
`local_grid`, `other_grid` | as the control |
| `RestServer` | `GET /meta/book/probe_book/tree?package=all` | **200,
the implicit book: label `probe_book`, no entries** | the declared book
`Probe Book` with `probe_page` | as the control |
| `RestServer` | `GET /meta/diagnostics?type=view&package=all` |
**`scannedItems: 0`** | `scannedItems: 3`, both packages in `stats` | as
the control |
| `RestServer` | item read, `?state=draft` read, `PUT ?package=all` | as
the control (folded by #22185) | | unchanged |
| catch-all | `GET /meta/view/case_grid/layers?package=all` | **404
`RESOURCE_NOT_FOUND`** | 200, overlay `live` | as the control |
| catch-all | `GET /meta/view?package=all` | **`[]`** | the three views
| as the control |
| catch-all | `GET /meta/book/probe_book/tree?package=all` | **the
implicit book, no entries** | the declared book | as the control |
| catch-all | `GET /meta/view/case_grid?package=all` | **200, but the
item is decorated `_provenance: 'org'` with no `_packageId`** |
`_packageId: 'com.probe.pkg'` | as the control |
| catch-all | `GET /meta/view/case_grid?state=draft&package=all` | **404
`NO_DRAFT`** | 200, the draft | as the control |
| catch-all | `PUT /meta/view/fresh_grid?package=all` | **row
`package_id: 'all'`** (the seat's pointer re-measured) | `RestServer`:
`package_id` null | `package_id` null |
| catch-all | `POST /meta/view/case_grid/publish?package=all` | 404
`ROUTE_NOT_FOUND`, zero protocol calls | | unchanged: the dispatcher
serves no publish, so it has no `?package=` to read |
**H2, what "no package" means at the list.** Measured on both hosts:
`GET /meta/view` without `?package=` lists `case_grid` (in
`com.probe.pkg`), `other_grid` (in `com.other.pkg`) and `local_grid`
(env-local). No package already means every package, so
`metaItemPackageBinding` is the right fold for the list. The list needs
no reading of `all` of its own.
**H3, `:5807` and `:6582`** (line numbers on `31d24a52`).
- `:5807` is `GET /meta/diagnostics`, the cross-type spec-validation
sweep. It hands `?package=` to `getMetaDiagnostics`, which runs
`getMetaItems({ type, packageId })` for every type it sweeps. That is a
list read keyed by package, so it is in the family. `all` reached it as
a literal (`scannedItems: 0`, above), so it is folded and pinned.
objectui at the `.objectui-sha` pin (`a58626c8`) sends that door's
`?package=` only after validating it against the project's packages
(`DirectoryPage.tsx`, `StudioHomePage.tsx`), so no first-party caller
sends `all` there.
- `:6582` is the item read's cache bypass, `packageScoped`. It is not a
read keyed by package: it asks whether the raw parameter is present, to
pick the uncached arm, and since #22185 that arm reads through the fold.
So `all` reaches it as a literal by design, and `?package=all` keeps the
uncached arm and its served `version`. Folding it would move
`?package=all` into the cached arm, which serves no `version`. It is not
changed here, and the fold's docblock now says why.
**H4, the export.** Kept in `rest-server.ts` and exported from the
package entry: `export { metaItemPackageBinding } from
'./rest-server.js'`. Not moved beside `metaSaveRequestOptions`, for two
reasons.
- The fold serves read doors (the layered read, the list, the book tree,
the sweep and the item read) as well as the save and the publish.
`meta-save-request.ts`'s header scopes that module to the save's
precondition and lifecycle, so the fold does not belong there.
- The entry already loads `rest-server.js` for `RestServer`, so the
export adds no module to the entry's closure.
**H5, Studio reach (triage's p1 condition): the save half does not
hold.** Read at objectui `a58626c8`,
`packages/app-shell/src/views/metadata-admin/ResourceEditPage.tsx`:
- The save and the publish fold `all` themselves
(`readActivePackageBinding`, lines 248 to 255, used by `doSave` at 1493
to 1498 and `doPublish` at 1685 to 1688), so Studio's save omits
`?package=` under the "show everything" scope.
- The layered and draft reads send the raw router `?package=`
(`ownerPackageId`, lines 433, 1009 to 1015 and 1502 to 1505), so they
send `?package=all`. Through the catch-all they reach the dispatcher's
layered and item branches, which this PR folds.
- Whether the hosted runtime serves `/meta` through that catch-all:
**NOT MEASURED** from this session (objectstack-ai/cloud is not readable
here).
## Pins (`packages/runtime/src/domains/meta-package-all-fold.test.ts`)
One file drives both hosts over real stores, as
`meta-save-preconditions-parity.test.ts` does. The `RestServer` leg
calls its registered handlers. The catch-all leg repeats the catch-all's
four statements over a real Fetch `Request` into the real
`HttpDispatcher.dispatch()`. Each row compares a door's answer to
`?package=all` with the same door's answer without `?package=`, and
keeps a scoping control beside it.
- Per host: the layered read (control: `?package=com.probe.pkg` serves
the same layers, `?package=com.other.pkg` answers 404), the list
(control: each real package id lists only its own view), the book tree
(control: the owning package resolves the declared book, another package
falls to its implicit book), the item read, the `?state=draft` read of a
draft that inherited the item's package, and the save (control: `PUT
?package=com.probe.pkg` binds the row).
- `RestServer` alone: the diagnostics sweep (control: a real package id
sweeps fewer items).
## Reverse verification and ablations
The fix was committed first. Every leg was restored from `HEAD` and
proven by blob hash and an empty `git diff HEAD`. The subject resolves
by relative source import (`meta.ts`) and `@objectstack/rest` through
the runtime vitest alias to source, so no `dist/` leg applies.
| leg | result | rows red |
|:--|:--|:--|
| this branch | 13 passed | none |
| base `rest-server.ts`, `index.ts` and `meta.ts` in the tree | 10
failed, 3 passed | every row except `RestServer`'s item read, draft read
and save, which #22185 already folded |
| ablation A: `RestServer`'s layered read back to `req.query?.package or
undefined` | 1 failed, 12 passed | `RestServer`'s layered read |
| ablation B: the dispatcher's item branch back to the raw read | 3
failed, 10 passed | the catch-all's item read, draft read and save |
The ablations went through `node scripts/ablation-replace.mjs`. A:
anchor x1 to x0, blob `4f3ef5968792` to `905c48c396c2`, restored to
`4f3ef5968792` == HEAD. B: anchor x1 to x0, blob `b21893a63ef8` to
`88f1ec5f41a7`, restored to `b21893a63ef8` == HEAD.
## Verification
All at this branch's head `db727d0d` (`git rev-parse --short HEAD`),
base `ffb31fca`, except where a line names `a13568b4` (the fix commit;
`db727d0d` adds only the draft-read row to the pin file). Heavy runs
went through `scripts/pm/os-verify-lock.sh`.
- **Build.** `pnpm --filter '@objectstack/runtime^...' build` (the
dependency closure), then `@objectstack/rest` and `@objectstack/runtime`
rebuilt at head: exit 0. The built entries load:
`require('packages/rest/dist/index.cjs')` gives 35 exports,
`metaItemPackageBinding` among them (it answers `undefined` for `all`,
the empty value and an array, and `com.probe.pkg` for `com.probe.pkg`);
the ESM entry exports it too;
`require('packages/runtime/dist/index.cjs')` gives 312 exports.
- **Typecheck.** `pnpm --filter @objectstack/rest typecheck`: exit 0.
`pnpm --filter @objectstack/runtime typecheck`: exit 0 after the rest
rebuild (before it, the stale `dist/index.d.ts` answered TS2305 for the
new export, as expected). The test layer printed `check:test-typecheck:
OK — 27 file(s) / 190 error(s) / 68 pinned signature(s) held`, ledger
unchanged. The new pin file is in that program (`tsc -p
tsconfig.test.json --listFiles` lists it) with no error of its own.
- **Unit tiers, at `a13568b4`.** `pnpm --filter @objectstack/rest exec
vitest run --project local`: 262 files passed, 4938 passed, 326 skipped.
`pnpm --filter @objectstack/runtime exec vitest run --project local`:
336 files passed, 4747 passed, 19 skipped. The `repo` projects are CI's,
except `meta-list-projection-parity.test.ts`, which reads
`rest-server.ts`'s handler source and was run by name.
- **Named pins, at `db727d0d`.** `meta-package-all-fold.test.ts` 13
passed; with `meta-save-preconditions-parity.test.ts`, 26 passed.
`meta-draft-head-package-inheritance.test.ts` and
`meta-draft-read-door-census.test.ts`: 13 passed.
`meta-list-projection-parity.test.ts` (`--project repo`): 682 passed.
- **Gates.** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` at `db727d0d` derives 64 commands
(6 paths, 420 changed lines, under the 5000 threshold), the same 64 the
order named. Each was run with its exit code recorded. `--ran`
reconciliation: `64 derived famil(ies) accounted for — 63 run, 1
NOT-MEASURED`, 0 unrun. 63 exit 0. The full `pnpm lint` (`eslint .
--no-inline-config`, whole repository) exits 0 with no findings.
- **NOT MEASURED: `pnpm check:dual-build-cjs-loads`, reason: exit 3
`PREREQUISITE NOT MET`** (its self-test passed, 93 cases). It reads
every package's `dist/`, and 36 packages are unbuilt in this worktree.
The declared narrowing in its place is the CJS and ESM entry loads of
the two packages whose shipped code changed (above). CI runs the full
gate.
- **Upstream.** `origin/main` is 5 commits past the base (`9f0de32a`).
None touches `packages/rest`, `packages/runtime` or
`packages/metadata-protocol`, so no merge of `main` was taken.
## Acceptance notes
- **Repeated `?package` on the dispatcher.** The dispatcher's `/meta`
domain does not judge query multiplicity, as #22141 recorded. The
catch-all flattens its query to one string per name, so this changes
nothing there. A Node host that hands an array used to forward the array
to the store as the package id; it now reads it as naming no package, as
`RestServer`'s fold reads a non-string.
- **`GET /meta/_drafts?packageId=`** (`RestServer` and the dispatcher)
reads a differently named parameter, `packageId`, and forwards it raw.
It is not a `?package=` reader, and no caller sending `all` there was
measured, so it is not in this family and is not changed here.
- The dispatcher serves no `POST /meta/:type/:name/publish` (404
`ROUTE_NOT_FOUND`, zero protocol calls), so it has no publish door to
fold.
Written by session `session_01RWZbGvPFcRKvUqASZtunCU`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0b997ea commit fbcbcf1
6 files changed
Lines changed: 409 additions & 11 deletions
File tree
- .changeset
- packages
- rest/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 | + | |
| 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 | |
|---|---|---|---|
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1736 | 1736 | | |
1737 | 1737 | | |
1738 | 1738 | | |
| 1739 | + | |
| 1740 | + | |
| 1741 | + | |
| 1742 | + | |
| 1743 | + | |
| 1744 | + | |
| 1745 | + | |
| 1746 | + | |
| 1747 | + | |
| 1748 | + | |
| 1749 | + | |
| 1750 | + | |
1739 | 1751 | | |
1740 | | - | |
| 1752 | + | |
1741 | 1753 | | |
1742 | 1754 | | |
1743 | 1755 | | |
| |||
3920 | 3932 | | |
3921 | 3933 | | |
3922 | 3934 | | |
3923 | | - | |
| 3935 | + | |
| 3936 | + | |
3924 | 3937 | | |
3925 | 3938 | | |
3926 | 3939 | | |
| |||
5804 | 5817 | | |
5805 | 5818 | | |
5806 | 5819 | | |
5807 | | - | |
| 5820 | + | |
| 5821 | + | |
| 5822 | + | |
5808 | 5823 | | |
5809 | 5824 | | |
5810 | 5825 | | |
| |||
6045 | 6060 | | |
6046 | 6061 | | |
6047 | 6062 | | |
6048 | | - | |
| 6063 | + | |
| 6064 | + | |
| 6065 | + | |
6049 | 6066 | | |
6050 | 6067 | | |
6051 | 6068 | | |
| |||
6379 | 6396 | | |
6380 | 6397 | | |
6381 | 6398 | | |
6382 | | - | |
| 6399 | + | |
| 6400 | + | |
6383 | 6401 | | |
6384 | 6402 | | |
6385 | 6403 | | |
| |||
0 commit comments