Skip to content

Commit 072da7b

Browse files
dmealingclaude
andcommitted
docs: changelog + 1.0 migration §12 for the three estate-found fixes
The forge deregistration is a load-breaking change for a project carrying the vocabulary, so it gets a numbered section in the guide an adopter actually reads, with both migrations spelled out — strip with `meta upgrade --apply`, or opt the provider in — and the reason `metamodelVersion` does not move. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NTcEKXTQMYt84fAjuw5A2M
1 parent cda3419 commit 072da7b

2 files changed

Lines changed: 124 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,93 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
77

88
## [Unreleased]
99

10+
### BREAKING — the default provider composition registered vocabulary no other port has
11+
12+
`forgeTypesProvider` leaves `defaultLoadMemoryProviders` and `meta types`. Metadata carrying
13+
`@forgeConfidence` — or a `decision` / `principle` / `convention` / `glossary` / `failure`
14+
node — now fails to load unless the project opts the provider in.
15+
16+
That vocabulary was registered in **TypeScript and nowhere else**: no C#, Python, Java or
17+
Kotlin registry declares a line of it, and `expected-registry.json` — the manifest all five
18+
ports byte-match — carries none of it, so the cross-port gate never had jurisdiction over
19+
any of it. One document therefore had **two verdicts depending on which toolchain read it**,
20+
which is precisely the defect the `0.25.0` breaking slot was spent on, and 1.0 would have
21+
frozen it into a permanent public surface. Found by an adopter estate running the RC, in a
22+
project carrying nine such nodes. **`metamodelVersion` does not move** — none of this was
23+
ever in the manifest, so the covered contract is unchanged; what changed is which door
24+
agrees with it.
25+
26+
The provider is still exported and unchanged: `loadMemory(root, { providers:
27+
[forgeTypesProvider] })` is the chartered ADR-0023 opt-in, and a project taking it knows it
28+
has a surface the other four ports do not. `meta upgrade --apply` strips the attributes.
29+
Migration: [`0.x-to-1.0.md` §12](docs/features/migrations/0.x-to-1.0.md).
30+
31+
**The gate closes the door, not the instance.** `metadata`'s registry-conformance test pins
32+
the canonical against `coreProviders` — but the door adopters actually use composed
33+
something else, so the gate was asserting about a provider list that was not the one in
34+
force. A new sdk test asks the DEFAULT composition the same question, and is proved
35+
non-vacuous with the provider that escaped it. The repo's own fixture was part of how this
36+
stayed invisible: `trainer-website-meta` carried a `decision.global` node and eight
37+
`@forge*` attrs, so the CLI suite exercised TS-only vocabulary on the default path.
38+
39+
Also removed from `@metaobjectsdev/sdk`: the forge memory record schemas (`RecordCore`,
40+
`DecisionRecord`, …) and their file storage layer (`readRecord` / `writeRecord` /
41+
`listRecords` / `promoteRecord` / `supersede` / …). Nothing in the library imported either,
42+
and a sweep of all seven adopter estates found no use of any of the fifteen names.
43+
44+
### Fixed — a hand-edited `<Entity>Names` read as "in sync with the metadata"
45+
46+
Hand-edit a generated `Subscriber.names.ts` so a column reads `first_name_BROKEN` and
47+
`meta verify --codegen` exited **0**, saying *"generated output is in sync with the
48+
metadata"* — over a file contradicting the model, in the one artifact whose entire purpose
49+
is that a physical name is spelled once per run and referenced everywhere else. Found by an
50+
adopter estate doing what its brief said: breaking the gate to see whether it convicts.
51+
52+
**RULED: a names artifact must match a fresh regen exactly.** If you want a constant of your
53+
own, put it in a file of your own. The `0.24.3` hand-edit exemption is otherwise untouched
54+
and remains right — it exists because `meta gen` MERGES an edit and reports `merged`, so
55+
convicting it printed a remedy that could not work. A names artifact is not that shape:
56+
every value is a string, so no compiler sees a wrong one, nothing reads it in a test, and an
57+
edit inside it silently renames a column for every consumer.
58+
59+
Two smaller claims in the same area were false and are corrected: the success line now says
60+
what it CHECKED (*"every file's generated contribution is current"*) rather than the stronger
61+
property it had not verified, and `--codegen`'s `--help` no longer describes the pre-`0.24.3`
62+
gate it replaced. `.names.ts` is now one exported constant the emitter, the ejectable
63+
reference template and the gate all spell from — three literals would have been three
64+
chances for the gate to stop finding the artifact it protects, silently. An adopter who owns
65+
the generator and renames the artifact opts out; that is stated in the reference template
66+
rather than hidden.
67+
68+
### Fixed — the one `@expr` spelling a human writes was the one that drifted
69+
70+
`pg_get_expr` re-emits every operator SPACED and appends casts, so an index authored as
71+
`(request_context->>'device_id')` reads back as `((request_context ->> 'device_id'::text))`.
72+
The comparator already canonicalized the casts and the redundant parens — measured across
73+
five spellings against a live engine, three reconciling and two not — so the ONE surviving
74+
difference was the whitespace around the operator, and it was the one difference a person
75+
actually produces. `verify --db` reported drift and `migrate` proposed **DROP + CREATE
76+
against an index the database already held**, declared exactly as it stood. Found by an
77+
adopter estate running the RC: its own README said to retire a hand-written index adjunct
78+
"the moment #342 ships", and following that instruction proposed a DROP against a live
79+
production index.
80+
81+
Whitespace around a symbolic operator run now collapses, outside single-quoted literals, in
82+
the pass that already did that for commas. Safe on the stronger ground the comparator now
83+
needs: it is not semantic in ANY valid SQL — where two spellings differ only there, at most
84+
one parses, because Postgres lexes a maximal run of operator characters as a single token.
85+
Only SYMBOLIC operators, so `AND` / `OR` / `IN` / `IS NULL` keep their separators. Derived
86+
from Postgres' operator CHARACTER SET rather than a list of operators, so `->>`, `!~*`,
87+
`@>`, `?|` and anything user-defined normalize by one rule.
88+
89+
The file's header claimed the canonicalizer was safe because "every check expression we emit
90+
is machine-derived … there is no arbitrary author SQL to mis-normalize" — which stopped
91+
being true when `index.lookup`'s `@expr` and `@where` began using it. It now justifies each
92+
rewrite on the ground it actually needs. Gated against a real engine, per this package's
93+
doctrine: the test asserts that PG really does re-emit the expression spaced and cast (so a
94+
future PG rendering it differently fails there rather than making the suite vacuous), diffs
95+
all five spellings to empty, and proves the gate can still convict a genuinely different key.
96+
1097
### Fixed — `verify --docs` skipped a page and said nothing about it
1198

1299
`--docs` tests BOTH halves against `.gitignore` — the pages a fresh `meta docs` emits, and the

‎docs/features/migrations/0.x-to-1.0.md‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,43 @@ Importing the built-in generators from `@metaobjectsdev/codegen-ts/generators`
326326
#203 escape hatch silently. `meta eject queries` (over your copy, re-applying any
327327
customization) restores it.
328328

329+
## 12. The forge vocabulary leaves the default composition (at the 1.0 cut)
330+
331+
`@forgeConfidence`, `@forgeSource` and the other 18 `@forge*` attributes, plus the five
332+
top-level types `decision` / `principle` / `convention` / `glossary` / `failure`, are **no
333+
longer registered by default**. Metadata carrying any of them fails the load with
334+
`ERR_UNKNOWN_ATTR` (attributes) or an unknown-type error (nodes).
335+
336+
- **Why:** they were registered in **TypeScript and nowhere else**. No C#, Python, Java or
337+
Kotlin registry declares a line of it, and `expected-registry.json` — the manifest all
338+
five ports byte-match — carries none of it, so the cross-port gate never had jurisdiction.
339+
One document had two verdicts depending on which toolchain read it. That is the defect the
340+
`0.25.0` line was spent on, and 1.0 would have frozen it into a permanent public surface.
341+
342+
- **Migration, if you do not want the vocabulary:** `meta upgrade --apply` strips the
343+
attributes. Delete any `decision` / `principle` / `convention` / `glossary` / `failure`
344+
node, or move what it recorded into `notes` on a node that survives.
345+
346+
- **Migration, if you DO want it:** opt the provider in — the chartered ADR-0023
347+
consumer-provider path. It is still exported and unchanged:
348+
349+
```ts
350+
import { loadMemory, forgeTypesProvider } from "@metaobjectsdev/sdk";
351+
const root = await loadMemory("./", { providers: [forgeTypesProvider] });
352+
```
353+
354+
Opting in means knowingly taking a surface the other four ports do not have. That is the
355+
whole distinction being drawn: the DEFAULT composition is the cross-port surface, and a
356+
consumer provider is a choice with consequences you can see.
357+
358+
- **`metamodelVersion` does not move.** None of this was ever in `expected-registry.json`,
359+
so the covered metadata contract is unchanged; what changed is which door agrees with it.
360+
361+
- **Also removed from `@metaobjectsdev/sdk`:** the forge memory record schemas
362+
(`RecordCore`, `DecisionRecord`, …) and their file storage layer (`readRecord`,
363+
`writeRecord`, `listRecords`, `promoteRecord`, `supersede`, …). Nothing in the library
364+
and no adopter estate imported them.
365+
329366
## Additive in 1.0 (no migration required)
330367

331368
- **`field.uri` / `field.inet` subtypes + `@stringFormat`** (`email` / `hostname`)

0 commit comments

Comments
 (0)