Skip to content

Commit 1b44b6d

Browse files
committed
chore: changeset for the /meta list runtime-baseline dedup (#7654)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WMpuAfA2KSdDjGF6tm1bH
1 parent 18e120e commit 1b44b6d

1 file changed

Lines changed: 51 additions & 0 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
"@objectstack/metadata-protocol": patch
3+
---
4+
5+
fix(metadata-protocol): `GET /api/v1/meta/<type>` stops listing a skill twice after a runtime PUT (#7654)
6+
7+
`PUT /api/v1/meta/skill/<name>` returned 200 and then `GET /api/v1/meta/skill`
8+
served the skill **twice** — the store-override row and the package row, side by
9+
side, disagreeing about `active`. Nothing about the pair told a caller which one
10+
was the effective document.
11+
12+
`getMetaItems` merges three layers, and two of them answered the identity
13+
question differently:
14+
15+
- `mergePackageAwareOverlay` — the `sys_metadata` overlay merge — resolves per
16+
`(slot, package)` and treats a **package-less** row as *standing in for* each
17+
package's row of that name, which is exactly how
18+
`getMetaItem(name, packageId=P)` resolves.
19+
- the MetadataService merge one layer below keyed a hand-rolled `Map` on
20+
`(package, name)` with **strict** equality, so a package-less row occupied a
21+
slot of its own instead of standing in for anything.
22+
23+
A runtime PUT carries no `?package=`, so the row it writes is
24+
`package_id IS NULL`. For a type whose baseline arrives through the
25+
MetadataService rather than the SchemaRegistry — `skill`, `agent`, `tool` reach
26+
it through that service's own loaders — the registry listing is empty, so the
27+
overlay merge had no base row to take provenance from and left the override body
28+
with no `_packageId`. Its key then missed the package-bearing baseline row in
29+
the merge below, the "already present, do not overwrite" guard never fired, and
30+
both rows were served.
31+
32+
The MetadataService merge now runs **that same package-aware resolution** rather
33+
than a second implementation of it: the runtime listing is the base layer and
34+
the registry-plus-overlay result is the higher one, so the documented precedence
35+
(a `sys_metadata` customization wins over the artifact baseline) is preserved
36+
while the two steps can no longer disagree about what a package-less row means.
37+
38+
**Not a `skill` special case.** The same shape was measured duplicating for
39+
`agent`, `tool` and `page`; the mechanism is the merge's attribution rule, not
40+
the type, and the fix closes the mirrored attribution too (a package-less
41+
baseline under a package-bearing higher row). Where a name is shipped by two
42+
packages, both rows are still served — ADR-0048 resolution is unchanged — and a
43+
package-less override now reaches both of their slots.
44+
45+
Also visible: the surviving row carries the `_packageId` of the package it
46+
overrides, so provenance, the package filter and the disabled-package filter see
47+
an override the way they already see a registry item.
48+
49+
Unaffected: i18n bundles (`email_template`) keep every locale — the slot, and so
50+
the discriminator, is computed by the same function either way — and a type with
51+
no `metadata` service installed takes the same path it always did.

0 commit comments

Comments
 (0)