From 5e7b1aad78a6ec26b1684eabfae7757d8ffa147c Mon Sep 17 00:00:00 2001 From: aaltshuler Date: Mon, 25 May 2026 23:56:51 +0100 Subject: [PATCH] =?UTF-8?q?docs(schema):=20document=20enum=20migration=20(?= =?UTF-8?q?widen/narrow/String=E2=86=94enum)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an "Enum evolution" section to schema-language.md covering the four supported shapes and their tiers, plus the unsupported cases (non-String scalar change, interface enums, in-place variant rename). Record the new ChangeEnumConstraint migration step. Add OG-MF-105 / OG-MF-107 to the schema-lint code table and clarify OG-MF-106 as a genuine scalar change. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/user/schema-language.md | 14 ++++++++++++++ docs/user/schema-lint.md | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/user/schema-language.md b/docs/user/schema-language.md index 4250676..ce974b0 100644 --- a/docs/user/schema-language.md +++ b/docs/user/schema-language.md @@ -75,10 +75,24 @@ Edge bodies only allow `@unique` and `@index`. - `AddConstraint { type_kind, type_name, constraint }` - `UpdateTypeMetadata { … annotations }` - `UpdatePropertyMetadata { … annotations }` +- `ChangeEnumConstraint { type_kind, type_name, property_name, to_property_type, code }` — evolve an enum-typed property's value-set (see below) - `UnsupportedChange { entity, reason }` (forces `supported=false`) `apply_schema()` returns `SchemaApplyResult { supported, applied, manifest_version, steps }` and is gated by an internal `__schema_apply_lock__` system branch so concurrent schema applies serialize. +## Enum evolution + +Enums are stored physically as `Utf8`; the allowed value-set lives only in the schema, not in the column. So enum migrations change catalog metadata, never the data — no table rewrite, and the manifest version does not advance. Four shapes are supported on **node and edge** properties (interface enum changes are not supported in v1): + +| Change | Example | Tier | Behavior | +|---|---|---|---| +| **Widen** (add variants) | `enum(open, closed)` → `enum(open, closed, archived)` | Safe | Metadata-only; applies unconditionally. No existing row can be invalid. | +| **`enum` → `String`** (loosen) | `enum(open, closed)` → `String` | Safe | Metadata-only; every enum value is a valid `String`. | +| **Narrow** (remove variants) | `enum(open, closed, archived)` → `enum(open, closed)` | Validated (`OG-MF-105`) | Apply scans existing rows; if any holds a removed value it **aborts** before publish, naming the offending value. No data is dropped — fix or migrate the rows, then re-apply. | +| **`String` → `enum`** (constrain) | `String` → `enum(open, closed)` | Validated (`OG-MF-107`) | Apply scans existing rows; aborts on the first out-of-set value. | + +Reordering variants is a no-op (the value-set is sorted + deduped, so `enum(b, a)` and `enum(a, b)` are identical). Changing an enum to a non-`String` scalar (e.g. `enum(...)` → `I32`), or changing nullability/list-ness alongside the value-set, is a genuine type change and stays `UnsupportedChange` (`OG-MF-106`). Renaming a variant in place (a value remap, e.g. `closed` → `done`) is not yet supported — model it as add-then-narrow with a data migration in between. + ## Destructive drops — `--allow-data-loss` `DropProperty` and `DropType` steps default to `Soft` mode: the catalog tombstones the entry but the prior column / dataset remains time-travel-reachable via `snapshot_at_version(prev)` until `omnigraph cleanup` runs. Soft drops are reversible. diff --git a/docs/user/schema-lint.md b/docs/user/schema-lint.md index a1495fd..638c3be 100644 --- a/docs/user/schema-lint.md +++ b/docs/user/schema-lint.md @@ -35,7 +35,9 @@ The chassis defines ten families. Today only DS and MF have emitted codes. The r | `OG-DS-105` | Destructive | destructive | error | drop populated vector column (reserved) | | `OG-MF-103` | Maybe-fail | validated | error | add required property without `@default` to populated type | | `OG-MF-104` | Maybe-fail | validated | error | tighten nullable to non-nullable (reserved) | -| `OG-MF-106` | Maybe-fail | destructive | error | narrowing scalar type | +| `OG-MF-105` | Maybe-fail | validated | error | narrow enum value set (remove allowed variants) | +| `OG-MF-106` | Maybe-fail | destructive | error | narrowing scalar type (genuine scalar change only) | +| `OG-MF-107` | Maybe-fail | validated | error | constrain `String` to `enum` | The full code catalog source of truth lives in `crates/omnigraph-compiler/src/lint/codes.rs`. CI-level invariants (uniqueness, format, family coverage) are unit-tested in the same module.