Skip to content

Commit 1ebd82c

Browse files
dmealingclaude
andcommitted
feat(gates): shipped metadata examples must load under the strict registry (#337)
Three times a doc or an agent-context skill taught vocabulary the loader had already retired, so metadata written by following our own instructions failed to load — and an adopter found it every time. #337 described @Verifiedby as live after FR-038 retired it; #342 gave @fields together with @expr as a worked example, the exact spelling that release made a load error; #343 taught @Verifiedby and the pre-0.24.0 @status enum a full release after both went. Each was fixed by hand in a different file, so the family recurred rather than converging. check-doc-examples.ts loads every fenced JSON example under docs/ and the agent-context skills against the strict registry, in the gates lane. The hard part is telling a real drift from a deliberately partial illustration, and the rule is the KIND of error rather than a marker anyone has to remember: fail on errors about vocabulary the block USES (an attribute that no longer exists, a value outside its enum, an illegal combination — wrong at any size); allow errors about what it OMITS or REFERENCES (an elided required attribute, an extends target in the next block — that IS fragment-ness). Fragments are wrapped in a synthetic host and the fields their @fields names are synthesised with it, so the scaffolding cannot manufacture a finding the document never committed. An unclassified error code stops the gate instead of defaulting either way: one default widens the blind spot silently, the other floods every fragment. That fired immediately on ERR_ENUM_INT_VALUE_MAP_ARRAY, which is not in the main error ledger. docs/superpowers/ and docs/features/migrations/ are out of scope because they are records, not instructions — a migration guide's purpose is to show the retired spelling beside its replacement, and editing it to satisfy a gate would falsify it. One narrow opt-out marker covers the provider-extension recipes, counted in the summary so a blind spot cannot grow unseen. test-doc-examples.ts replays all three incidents and asserts the gate rejects each, and that a fragment, an unresolved reference and a config block stay quiet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhpswkF1NvwxhFWMmdAT15
1 parent d215568 commit 1ebd82c

5 files changed

Lines changed: 604 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,46 @@ extracting the authored examples from shipped docs, skills and fixtures and load
284284
under a strict loader — is tracked separately; `meta upgrade`'s retirement map is already
285285
the natural source of truth for "what must no longer appear in an example".
286286

287+
### Added — every shipped metadata example is now gated against the strict registry ([#337](https://github.com/metaobjectsdev/metaobjects/issues/337))
288+
289+
**The same failure has now landed three times, and an adopter found it every time.** A doc
290+
or an agent-context skill taught vocabulary the loader had already retired, so metadata
291+
written by following our own instructions did not load: **#337** described `@verifiedBy` as
292+
live a release after FR-038 retired it; **#342** gave `{"@fields": [...], "@expr": ...}` as a
293+
worked example, the exact spelling that release turned into a load error; **#343** taught
294+
`@verifiedBy` and the pre-`0.24.0` `@status` enum a full release after both went. Each was
295+
fixed by hand, in a different file — which is why the family recurred instead of converging.
296+
297+
`scripts/check-doc-examples.ts` now loads every fenced JSON example under `docs/` and the
298+
`agent-context` skills against the **strict** registry, in the `gates` lane.
299+
300+
**The hard part was never extraction, it was telling a real drift from an illustration**,
301+
because most doc blocks are deliberately partial. The rule is the KIND of error, not a
302+
marker anyone has to remember: **fail on errors about vocabulary the block USES** (an
303+
attribute that no longer exists, a value outside its enum, an illegal combination — wrong at
304+
any size); **allow errors about what it OMITS or REFERENCES** (a required attribute elided, an
305+
`extends` target living in the next code block — that IS fragment-ness). A fragment is
306+
wrapped in a synthetic host, and the fields its `@fields` names are synthesised with it, so
307+
the scaffolding cannot manufacture a finding the document never committed. **An error code in
308+
neither list stops the gate as "unclassified"** rather than defaulting: one default silently
309+
widens the blind spot, the other floods hundreds of fragments — the same posture, for the
310+
same reason, as `VocabularyRewrite.otherwise`. It earned its keep immediately, stopping on
311+
`ERR_ENUM_INT_VALUE_MAP_ARRAY`, a code that is not in the main error ledger at all.
312+
313+
Two trees are deliberately out of scope, and the reason is the same one that makes deleting
314+
an `@status: abandoned` node data loss rather than a migration: **`docs/superpowers/` and
315+
`docs/features/migrations/` are records, not instructions.** A plan documents what was decided
316+
at a time, and a migration guide's whole purpose is to show the retired spelling beside its
317+
replacement — editing either to satisfy a gate would falsify it. One narrow opt-out marker
318+
(`<!-- meta-example: external-provider -->`) covers the provider-extension recipes, whose
319+
subject is an attribute the consumer registers themselves; it is counted in the gate's own
320+
summary, because a blind spot nobody can see growing is how this family started.
321+
322+
`scripts/test-doc-examples.ts` replays all three incidents as fixtures and asserts the gate
323+
rejects each one — and that a partial fragment, an unresolved reference and a plain config
324+
block stay quiet, since a gate that flags illustrations gets switched off and then catches
325+
nothing at all.
326+
287327
## [0.24.0] — npm `0.24.0` · PyPI `0.24.0` · NuGet `0.24.0` · Maven `7.24.0`
288328

289329
> ### ⚠️ BREAKING FOR METADATA AUTHORS — five vocabulary changes in ONE window

‎docs/recipes/extending-metaobjects-with-providers.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ tooling sees the same metamodel.
109109

110110
## 3. Author metadata that uses the new subtype
111111

112+
<!-- meta-example: external-provider -->
113+
112114
```jsonc
113115
// metaobjects/meta.toolcalls.json
114116
{ "metadata.root": {

0 commit comments

Comments
 (0)