Skip to content

Commit 20dcbdb

Browse files
committed
docs(1.0): B1 compat policy + B2 0.x->1.0 migration guide
B1 docs/compatibility-policy.md: the consumer-facing 1.0 promise — covered surface (metamodel vocab / canonical+YAML / wire / CLI / scaffold-and-own; breaking => major), what's not covered (generator internals, runtime helpers, reserved vocab), the Metamodel-spec-version + decouple scheme, and the minor-vs-patch rule. B2 docs/features/migrations/0.x-to-1.0.md: consolidated migration — version re-baseline (npm 1.0.0 / Maven 8.0.0), verify strict-default, jsonb parsed-value, timestamp instant-default + @localTime, @dbColumnType slim, index.*/@unique removal (links the detailed doc), deprecated codegen-ts/generators export removal. FR-024 deferred. Readiness B1/B2 marked done. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
1 parent 812e574 commit 20dcbdb

3 files changed

Lines changed: 186 additions & 9 deletions

File tree

docs/1.0-readiness.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,14 @@ Status legend: ✅ done · 🔶 in progress · ⬜ not started · ❓ **[RATIFY]
3838

3939
## B. Compatibility surface (the 1.0 promise)
4040

41-
-**B1. Publish the compat policy** (ADR-0035 §1, consumer-facing): what semver
42-
covers post-1.0 — metamodel vocabulary, canonical/YAML format, wire/normalization,
43-
CLI surface, scaffold-and-own contract — and what it does *not* (generator
44-
internals, runtime helper internals, reserved/experimental vocab).
45-
-**B2. Write the `0.x → 1.0` migration guide.** Consolidate the breaking changes
46-
adopters must absorb: verify strict-default, jsonb open-bag parsed-value, the
47-
metamodel-1.0 vocabulary program, `index.*`/`@unique` removal (ADR-0040), and the
48-
deprecated-export removal (A3=remove). FR-024 is deferred (A2), so no declared-API
49-
vocab to migrate.
41+
-**B1. Compat policy published**[`docs/compatibility-policy.md`](compatibility-policy.md):
42+
the covered surface (breaking ⇒ major), what is not covered, the Metamodel-spec-version
43+
+ decouple scheme, and the MINOR-vs-PATCH rule. *(Consumer-facing form of ADR-0035 §1.)*
44+
-**B2. `0.x → 1.0` migration guide written**
45+
[`docs/features/migrations/0.x-to-1.0.md`](features/migrations/0.x-to-1.0.md):
46+
version re-baseline + verify strict-default, jsonb parsed-value, timestamp
47+
instant-default, `@dbColumnType` slim, `index.*`/`@unique` removal, deprecated-export
48+
removal. FR-024 deferred → no declared-API vocab to migrate.
5049

5150
## C. Metamodel freeze (the durable spine)
5251

docs/compatibility-policy.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Compatibility policy
2+
3+
This is the stability promise MetaObjects makes at **1.0** (Metamodel 1.0). It defines
4+
what SemVer covers, what it does not, and how versions work across the language ports.
5+
The governing decision is [ADR-0035](../spec/decisions/ADR-0035-one-zero-stability-commitment-and-version-unification.md).
6+
7+
## The core idea: a spec version, not one package number
8+
9+
The durable, promised contract is the **Metamodel spec version** — e.g. *Metamodel
10+
1.0*. Every language port advertises which spec version it implements, and the
11+
cross-port conformance corpora verify that claim byte-for-byte. Package versions are
12+
**ecosystem-natural and independent**:
13+
14+
| | Package version at the 1.0 cut | Promise it carries |
15+
|---|---|---|
16+
| npm / PyPI / NuGet | `1.0.0` | implements **Metamodel 1.0** |
17+
| Maven Central (Java / Kotlin) | `8.0.0` | implements **Metamodel 1.0** |
18+
19+
The two package lines then move forward independently in their own registries; the
20+
**Metamodel spec version** is the number that communicates cross-language parity and
21+
carries the compatibility promise. (This is the OpenTelemetry / Protobuf-editions
22+
model. The Java `7.x → 8.0` step is a forward major — package versions never move
23+
backward, a hard rule on every registry.)
24+
25+
## What is covered (breaking change ⇒ next MAJOR)
26+
27+
After 1.0, a breaking change to any of the following requires a **new major of the
28+
Metamodel spec version** (Metamodel 2.0) and a major bump of every affected package:
29+
30+
- **The metamodel vocabulary** — the registered type / subtype / attribute set,
31+
enforced by `registry-conformance`. This is the durable spine.
32+
- **The canonical authoring + interchange format** — canonical JSON keyword/`@`-attr
33+
rules, sigil-free YAML, the `extends` / `@via` grammar, package `::` syntax.
34+
- **The wire / normalization contract** — the cross-port serialized form (currency
35+
minor units, pagination, the native-return-type contract, jsonb parsed-value).
36+
- **The CLI command surface**`init` / `gen` / `verify` and their *documented*
37+
flags, per port (`meta`, `dotnet meta`, `mvn metaobjects:*`, `metaobjects`).
38+
- **The scaffold-and-own contract** — what `meta init` scaffolds and the `Generator`
39+
interface owned templates implement.
40+
41+
## What is NOT covered (may change in a MINOR)
42+
43+
- **Generator internals and the reference templates themselves.** Generated code is
44+
yours and disposable — its internals are not a public API. See
45+
[own-your-codegen](features/own-your-codegen.md).
46+
- **Runtime-library helper internals.** Idiomatic per port; best-effort, not promised.
47+
- **Anything explicitly marked experimental or reserved** and not yet in the registry
48+
(e.g. the reserved-but-unregistered declared-API vocabulary `api.*`/`operation.*`/
49+
`binding.*`, and reserved index subtypes `index.fulltext`/`vector`/`spatial`).
50+
51+
## MINOR vs. PATCH (what a version bump means)
52+
53+
The trigger is **new public surface, not code size**:
54+
55+
- **MINOR** — adds surface a consumer can newly depend on: a new generated artifact,
56+
a new CLI flag, or a newly-supported metamodel member. Additive; never breaking.
57+
- **PATCH** — a bug fix or internal refactor with no new surface.
58+
- **Metamodel spec-version bump** — only when the shared vocabulary or wire/canonical
59+
contract itself changes. Most releases are per-port package moves that do *not*
60+
touch the spec version.
61+
62+
## Spec-version support across ports
63+
64+
All ports that ship a given release implement the **same** Metamodel spec version,
65+
verified by the shared conformance corpora (metamodel, render, persistence,
66+
api-contract, registry). A port's package version tells you its own fix/feature level;
67+
its declared `metamodelVersion` tells you the contract it honors. When they differ,
68+
the spec version is authoritative for cross-language interop.
69+
70+
## Pre-1.0 (today)
71+
72+
Until the 1.0 cut, the project is in `0.x` (npm/PyPI/NuGet) / `7.x` (Maven) and the
73+
public API is not yet frozen — breaking changes may ship in a minor, as they did
74+
through the `0.14``0.15` vocabulary-finalization window. The
75+
[1.0 readiness checklist](1.0-readiness.md) tracks the remaining path; the
76+
[0.x → 1.0 migration guide](features/migrations/0.x-to-1.0.md) consolidates the
77+
breaking changes adopters absorb at the cut.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Migrating `0.x``1.0` (Metamodel 1.0)
2+
3+
This guide consolidates every breaking change an adopter absorbs moving from the
4+
`0.x` / `7.x` line to the **1.0 stable** release (Metamodel 1.0). Most of these
5+
already shipped incrementally across `0.14``0.15`; the 1.0 cut is the moment to be
6+
on all of them. The stability promise that begins at 1.0 is the
7+
[compatibility policy](../../compatibility-policy.md).
8+
9+
## 0. Update the dependency version
10+
11+
Package versions **re-baseline** at the cut (both forward — see
12+
[ADR-0035 §2](../../../spec/decisions/ADR-0035-one-zero-stability-commitment-and-version-unification.md)):
13+
14+
| Ecosystem | From | To |
15+
|---|---|---|
16+
| npm / PyPI / NuGet | `0.15.x` | **`1.0.0`** |
17+
| Maven Central (Java / Kotlin) | `7.7.x` | **`8.0.0`** |
18+
19+
All ports at these versions implement **Metamodel 1.0**. There is no `1.0``7.x`
20+
overlap: the lines are independent and each only moves forward.
21+
22+
## 1. `verify` is strict-by-default (shipped 0.14.0)
23+
24+
`verify` now **fails** on unknown types / subtypes / attributes / children across all
25+
CLI ports, rather than silently passing.
26+
27+
- **If the gate now fails on your metadata:** the offending attr/child is not a
28+
registered metamodel member. Either correct it, or — for genuinely author-supplied
29+
properties — move it into the registered `attr.properties` bag (exempt from the
30+
strict-attr check). Errors name the exit: `ERR_UNKNOWN_ATTR` /
31+
`ERR_CHILD_NOT_ALLOWED` / `ERR_UNKNOWN_TYPE`.
32+
- **Escape hatch** (per port): `meta verify --lax` (TS), `metaobjects verify --lax`
33+
(Python), `dotnet meta verify --lax` (C#), `-Dmeta.lax=true` (Java/Kotlin Maven).
34+
`gen`/`docs` stay lax by default; the Java goals gate at generate-time too.
35+
36+
## 2. A jsonb open-bag is a parsed JSON value (shipped 0.14.0)
37+
38+
A `field.string` + `@dbColumnType: jsonb` now surfaces a **parsed JSON value** at the
39+
API boundary (TS `z.unknown()`, Python `Any`, Java/Kotlin/C# the native map/node),
40+
not a JSON string. The stored/wire form is unchanged.
41+
42+
- **Migration:** consumers that received a string and called `JSON.parse(...)` (or the
43+
per-port equivalent) must drop that parse — the value arrives already parsed.
44+
45+
## 3. `field.timestamp` is instant-by-default (shipped 0.15.0)
46+
47+
`field.timestamp` now maps to an **instant** (`timestamptz` / `Instant` /
48+
`DateTimeOffset` / timezone-aware `datetime`) by default.
49+
50+
- **To keep the old naive/local behavior:** add **`@localTime: true`** to the field.
51+
- **Retired:** `@dbColumnType: timestamp_with_tz` — the timezone-awareness is now
52+
derived from the subtype, so remove that escape.
53+
54+
```jsonc
55+
// before (0.14): // after (1.0):
56+
{ "field.timestamp": { { "field.timestamp": {
57+
"name": "createdAt", "name": "createdAt" }} // instant by default
58+
"@dbColumnType": "timestamp_with_tz"
59+
}} // ...or naive: add "@localTime": true
60+
```
61+
62+
## 4. `@dbColumnType` slim-and-derive (shipped 0.15.0)
63+
64+
`@dbColumnType` narrows to genuinely-physical escapes. **Array-ness is derived** from
65+
`isArray`, and the `*_array` column types (`uuid_array`, `text_array`) plus `@kind:
66+
text` are **dropped**.
67+
68+
- **Migration:** remove any `@dbColumnType: *_array` / `@kind: text`; use the field
69+
subtype + `isArray` (arrays derive their column type automatically).
70+
71+
## 5. `index.*` type + `identity.secondary` key-purity (shipped 0.15.1)
72+
73+
`@unique` is **removed** from `identity.secondary` (now inherently a *unique* key →
74+
a legacy `@unique` fails load with `ERR_UNKNOWN_ATTR`). A non-unique index moves to
75+
the new **`index.lookup`** type.
76+
77+
- **Full mechanical rewrite + the no-DDL-churn guarantee:**
78+
[identity.secondary → index.lookup](identity-secondary-to-index-lookup.md).
79+
80+
## 6. Deprecated `codegen-ts/generators` export removed (at the 1.0 cut)
81+
82+
Importing the built-in generators from `@metaobjectsdev/codegen-ts/generators`
83+
(`entityFile` / `queriesFile` / `routesFile` / `barrel`) is **removed** at 1.0.
84+
85+
- **Migration:** import the owned copies `meta init` scaffolds into
86+
`codegen/generators/*` and wire those in `metaobjects.config.ts`. See
87+
[own-your-codegen](../own-your-codegen.md). (If you scaffolded with a recent
88+
`meta init`, you already import the owned copies — no change needed.)
89+
90+
## Additive in 1.0 (no migration required)
91+
92+
- **`field.uri` / `field.inet` subtypes + `@stringFormat`** (`email` / `hostname`)
93+
for validated-string content — new, opt-in. Adopt where useful.
94+
- **Reverse navigation** via generated explicit FK finders — additive.
95+
- **`field.enum`, `index.lookup`, currency, M:N relationships** — all available.
96+
97+
## Not in 1.0: the declared-API surface (FR-024)
98+
99+
`api.*` / `operation.*` / `binding.*` are **deferred** post-1.0 (reserved but
100+
unregistered). Nothing to migrate; they can be added later without a breaking change.
101+
Derived CRUD remains the zero-config default. (Tracked in #10.)

0 commit comments

Comments
 (0)