Skip to content

Commit 618f70d

Browse files
claude[bot]claude
andauthored
fix(metadata-protocol): the authoring gate resolves references against runtime-authored metadata, not the boot-time registry (#16223)
* fix(metadata-protocol): the authoring gate resolves references against runtime-authored metadata, not the boot-time registry `RuntimeStackContext` declares its collections as the LIVE declarations, and live metadata has two homes: the SchemaRegistry that code packages fill at boot, and `sys_metadata` that every runtime author writes to. The per-write gather read only the first, so a `PUT /meta/dataset` that answered 200 was invisible to the very next `PUT /meta/dashboard`, which refused each widget bound to it with a phantom `widget-dataset-unknown` until the process restarted. Measured on the card's shape in one process, no restart: the row is in `sys_metadata`, `GET /meta/dataset` returns six datasets, the registry returns the five code-package ones, and the board collects three phantom refusals. The store half is now folded onto the registry half for every context collection, additively (a stored row contributes a name the registry lacks and never displaces a registry entry, whose `object` bodies are the resolved base-plus-extenders shape a raw row is not), active rows only, in the write's own organization partition, with a failed read reported rather than degraded silently into a smaller universe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ * test(metadata-protocol): the new engine double honours the caller's `limit`, and the ledger records its seams `check:objectql-double-limit` graded the harness's `find` double BLIND: it answered every row it matched however small a bound the caller passed, which reads exactly like a query that worked. The bound is now applied after the filter and by presence. `check:engine-double-contract` asked for the three seams this file pins (`delete`, `findOne`, `update`) to be recorded, so the ledger protects it: regenerated with `--write`, 3 rows added, 0 lost. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7bdb163 commit 618f70d

4 files changed

Lines changed: 608 additions & 20 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
"@objectstack/metadata-protocol": patch
3+
---
4+
5+
A dashboard bound to a dataset you just saved now publishes, without restarting the runtime.
6+
7+
The author-time gate that runs on every `active` metadata publish resolves a widget's `dataset` (and a `type: 'page'` view's `pageName`, and the sibling collections the cross-collection security rules compare against) against a resolution universe the host gathers per write. That gather read the SchemaRegistry alone. The registry is filled at boot by code packages, and for every metadata type except `object` a runtime write does not reach it — so a dataset saved through `PUT /api/v1/meta/dataset` was invisible to the gate until the process restarted, while `GET /api/v1/meta/dataset` returned it in the same instant with `_diagnostics.valid: true`.
8+
9+
Measured on the reported shape, in one process with no restart between the steps: the row is in `sys_metadata`, the read API lists six datasets, the registry lists the five code-package ones, and a three-widget board bound to the new dataset was refused `422` with three `widget-dataset-unknown` issues whose hint enumerated every dataset except the one just authored. The same request answered `200` after a restart, nothing else changed.
10+
11+
The gather now folds the stored half onto the registry half for every collection it carries. What that does and does not do:
12+
13+
- **Additive.** A stored row contributes a name the registry does not already carry and never displaces a registry entry — an object's registry copy is its resolved schema (base plus `extend` contributors) and a raw `sys_metadata` row is the base layer alone, so replacing it would trade this phantom for a subtler one. Where an org overlay redefines a code-package item, the gate still judges that item's content from the registry's version.
14+
- **Active rows only.** A draft does not resolve. The refuse-at-publish ruling exists so an author can write the widget first and the dataset second; a draft dataset that satisfied a published board would invert it.
15+
- **Scoped to the write's own partition** — environment-wide rows plus, when the write has one, its own organization. No other organization's overlays are visible to the gate, on any kernel.
16+
- **A failed store read is reported, not swallowed.** Context gathering still never fails a write, but a read that fails for any reason other than an unprovisioned `sys_metadata` now says so once, naming the consequence — a gather that silently shrinks is how a phantom refusal is manufactured in the first place.
17+
18+
The rules themselves are unchanged: a reference that resolves in neither home is still refused, with the same code, status and key path.

0 commit comments

Comments
 (0)