You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(spec)!: retire the standalone validation metadata kind (#4509, ADR-0088)
A validation rule authored as its own artifact bound to nothing and gated no
write. ValidationRuleSchema carries no object-binding key — no `object`, no
`objectName` — and all six variants are strictObject, so an author could not
supply one either. No merge step existed; the only code expecting such a key was
a reference-tracker row scanning a field the schema would have stripped.
Meanwhile the engine evaluates exactly one shape: the object's own
`validations[]`, on insert and on every matched update row.
So a rule created through the standalone door (*.validation.ts, or Studio's
Validations list) parsed, saved, reported success, and intercepted nothing —
including a state_machine rule, which ADR-0020 routes through this same
vocabulary. An author could believe they had locked down record state
transitions and have changed nothing.
Under ADR-0088 the kind fails the admission test on its first clause: a rule has
no independent lifecycle, because it only means something against an object. And
unlike the sibling disconnects in this batch it could not be bridged into one —
the shape has nowhere to name its object. Enforce-or-remove picks ENFORCE where
the feature exists and only wiring is missing (email_template), REMOVE where the
shape cannot carry the feature.
The rule VOCABULARY is untouched: ValidationRuleSchema and all six variants are
unchanged and fully live, and the engine's evaluation path is not modified. The
ledger keeps governing the schema through the gate's SPEC_ONLY_SCHEMAS override
(alongside webhook and query) — an ungoverned live schema is how the next drift
would hide.
Removed: registry entry (with its file patterns), MetadataTypeSchema member,
metadata-core lockstep enum member, schema-map entry, create seed, Studio nav
item + hand-crafted form + its four locale strings, and the dangling
reference-tracker row. Stored standalone rows are left alone — never evaluated,
so nothing changes behaviorally.
Also restamps validation.json's engine evidence lines (drifted ~220 lines) and
adds the ADR-0088 addendum recording the retirement.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5CYr5SDwe85gH2Jr5KSgu
Copy file name to clipboardExpand all lines: docs/adr/0088-metadata-kind-admission-and-retirement.md
+45Lines changed: 45 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -54,3 +54,48 @@ The cached remote-schema snapshot of a federated datasource (ADR-0062) has a rea
54
54
-`OPS_FILE_SUFFIX_REGEX` drops the four suffixes: `*.trigger.ts` / `*.router.ts` / `*.function.ts` / `*.service.ts` are no longer valid OPS metadata file names.
55
55
- The showcase's registry-driven `KIND_COVERAGE` shrinks in lockstep (its coverage test enforces exact registry membership); the four waivers disappear and `external_catalog`'s waiver becomes a permanent, documented exclusion.
56
56
- ADR-0005 / ADR-0010 prose tables no longer list the retired kinds.
57
+
58
+
## Addendum (2026-08): `validation` retired — the admission test's first clause
59
+
60
+
`validation` was registered as a kind with `allowRuntimeCreate: true`, a
61
+
`*.validation.ts` loader, and a Studio form. On the admission test above it
62
+
nonetheless fails the **first** clause — independent lifecycle — and the failure
63
+
is not cosmetic:
64
+
65
+
-**No independent lifecycle.** A rule only means something against an object,
66
+
and the only shape the engine evaluates is `object.validations[]`
67
+
(`evaluateValidationRules` on insert and on every matched update row).
68
+
-**No way to bind.**`ValidationRuleSchema` carries no `object` / `objectName`
69
+
key, and all six variants are `strictObject`, so an author could not supply
70
+
one either — the parse would reject it. There was no merge step, and the only
71
+
code that expected such a key was a reference-tracker row scanning a field
72
+
that could never exist.
73
+
74
+
So the standalone door led nowhere: an item authored through it — including a
75
+
`state_machine` rule, which ADR-0020 explicitly routes through this same
76
+
vocabulary — saved cleanly, reported success, and intercepted no write. That is
77
+
the ADR-0049 false-compliance shape, on a surface authors reasonably expect to
78
+
gate their data.
79
+
80
+
The kind is removed (registry entry, `MetadataTypeSchema` member, metadata-core
81
+
lockstep enum, schema map entry, Studio nav item and hand-crafted form, create
82
+
seed, and the dangling reference row). `ValidationRuleSchema` itself is
83
+
**unchanged and fully live** — it is the kind that was inert, not the
84
+
vocabulary. The liveness ledger keeps governing the schema through the gate's
85
+
`SPEC_ONLY_SCHEMAS` override, alongside `webhook` and `query`, precisely because
86
+
an ungoverned live schema is how the next drift would hide.
87
+
88
+
Note the contrast with the sibling disconnects closed in the same batch (#4509).
89
+
`email_template` had a real feature with missing wiring, so enforce-or-remove
90
+
resolved it by **enforcing** — a materializer bridge. `validation` had a shape
91
+
that could not carry the feature at all, so it resolves by **removing**. The
92
+
test is not "is this dead?" but "can this be made to work as declared?".
93
+
94
+
-`MetadataTypeSchema` and `DEFAULT_METADATA_TYPE_REGISTRY` shrink 26 → 25.
95
+
-`*.validation.ts` / `*.validation.yml` are no longer metadata file patterns.
96
+
(`OPS_FILE_SUFFIX_REGEX` never listed them — no change there.)
97
+
- Persisted standalone `sys_metadata` rows are left alone. They were never
98
+
evaluated, so nothing changes behaviorally; `migrateStoredMetadata` declines
99
+
them like any unregistered type.
100
+
-**Migration for authors:** move the rule into the object's `validations:`
101
+
array. The rule body is unchanged — same schema, same six variants.
'Also carries the validation-rule surface: rules are authored inline via object `validations` (the `validation` KIND was retired in #4509 — ADR-0088 — because a standalone rule had no way to name the object it validated). Every declared rule type is write-path enforced (rule-validator dispatches all of state_machine/script/cross_field/format/json_schema/conditional — ADR-0020 "no silent no-ops", closing the #1475 gap) and each is demonstrated: state_machine (task/project), script+cross_field (project), format/json_schema/conditional (account). Field-level requiredWhen/readonlyWhen are likewise enforced and demonstrated on invoice.',
57
59
},
58
60
field: {
59
61
status: 'demonstrated',
60
62
files: ['src/data/objects/field-zoo.object.ts'],
61
63
notes:
62
64
'FieldSchema is authored inline on objects (the stack DSL has no standalone `fields` collection); field-zoo exhausts every field type — see the variant-level test.',
63
65
},
64
-
validation: {
65
-
status: 'demonstrated',
66
-
files: [
67
-
'src/data/objects/account.object.ts',
68
-
'src/data/objects/task.object.ts',
69
-
'src/data/objects/project.object.ts',
70
-
'src/data/objects/invoice.object.ts',
71
-
],
72
-
notes:
73
-
'Authored inline via object `validations`. Every declared rule type is now write-path enforced (rule-validator dispatches all of state_machine/script/cross_field/format/json_schema/conditional — ADR-0020 "no silent no-ops", closing the #1475 gap) and each is demonstrated: state_machine (task/project), script+cross_field (project), format/json_schema/conditional (account). Field-level requiredWhen/readonlyWhen are likewise enforced and demonstrated on invoice.',
74
-
},
66
+
// `validation` was retired as a KIND in #4509 (ADR-0088) — the coverage test
67
+
// fails on any entry the registry no longer knows. The rules themselves are
68
+
// unchanged and still demonstrated; that coverage moved onto `object`, which
0 commit comments