Skip to content

FR-033: strict per-subtype metamodel + 5-concern provider split + metamodel docs + 5-port fan-out#29

Merged
dmealing merged 50 commits into
mainfrom
fr-033-strict-constraint-model
Jun 15, 2026
Merged

FR-033: strict per-subtype metamodel + 5-concern provider split + metamodel docs + 5-port fan-out#29
dmealing merged 50 commits into
mainfrom
fr-033-strict-constraint-model

Conversation

@dmealing

Copy link
Copy Markdown
Member

FR-033 — provider definitions as declarative data + strict per-subtype metamodel (GH #23)

Makes a provider's declarative metamodel definition (vocabulary + per-subtype constraints + descriptions + rule prose) live in single-sourced JSON (spec/metamodel/*.json) that every port reads, replacing the hand-coded-5× model. The metamodel becomes a strict, fail-closed allow-list (the pre-6.0 typesConfig model recovered), enforced in each port's validate(), with descriptions + the constraint graph emitted into the registry manifest and rendered as tiered LLM docs.

TypeScript (reference) — strict re-architecture

  • 5 concern providers (core / db / ui / prompt / documentation); only documentation is universal (*.*). ui/prompt attrs re-homed out of core into their own data-driven providers.
  • Strict per-subtype rules: each type.subType declares exactly its allowed children (attrs + structural) with cardinality; the "any attr" wildcard is gone. Misplaced attr → ERR_UNKNOWN_ATTR; misplaced structural child → ERR_CHILD_NOT_ALLOWED. All child rules live in the JSON (none hand-assigned in provider code), via extendsBase composition.
  • Registry manifest now carries description + rules/example/whenToUse + the children/parents constraint graph (coverage-gated).
  • Metamodel docs: meta docs --metamodel emits INDEX.md + per-type pages (full composed allowed attrs, provider-tagged) + a provider-ownership index, byte-gated.

Cross-port fan-out — Java, Kotlin, Python, C#

Each engine reads the SAME embedded spec/metamodel/*.json, registers descriptions + the strict constraint graph + strict per-subtype scoping (single-sourced, byte-identical to TS), grows its manifest emitter to the TS shape, and byte-matches fixtures/registry-conformance/expected-registry.json. The manifest is provider-agnostic, so the 5-concern split is an internal detail — but each port also has the identical ui/prompt provider split (metaobjects-ui/metaobjects-prompt, with a metaobjects-template back-compat alias).

All five engines now agree:

  • registry-conformance GREEN (byte-match the canonical) — TS / Java / Kotlin / Python / C#.
  • Identical ui/prompt provider split; the metaobjects-ui/metaobjects-prompt conformance fixtures pass everywhere.
  • Structural-child placement enforcement (ERR_CHILD_NOT_ALLOWED) parity, each gated by a test.
  • Full per-port suites green.

Notes for review

  • expected-registry.json grew (descriptions + constraint graph + strict scoping) — this is the deliberate cross-port contract; every port re-derives its registry from its embedded copy of the shared JSON and is asserted byte-equal.
  • metadata.root is the one hand-coded root-wrapper exception (not a provider-declared type) in each port.
  • Plans/specs: docs/superpowers/specs/2026-06-13-metamodel-self-description-design.md, docs/superpowers/plans/2026-06-14-fr-033-strict-constraint-model.md, docs/superpowers/plans/2026-06-14-fr-033-cross-port-fan-out.md.

🤖 Generated with Claude Code

dmealing and others added 30 commits June 13, 2026 15:51
Brainstormed + approved design for giving Claude Code / LLMs accurate,
context-tiered info about what every metadata type/subtype is for and
what each attribute means.

Net shape: a shared JSON descriptions source (core) embedded per port +
in-code descriptions (language/module-specific), attached to each port's
LIVE registry; each port dumps "registry-with-descriptions" — ONE
artifact that is both the registry-conformance gate target AND the
doc-gen input; one neutral Tier-2 engine renders TIERED markdown
(INDEX one-liners + per-provider detail pages) wired into the
metaobjects-authoring skill. Accurate-by-construction (derived from the
live registry), DRY (single source), conformance-gated (every entry
described; core byte-identical cross-port). Metaobjects applied to its
own metamodel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…model docs)

Reframed per design discussion: instead of a descriptions LAYER on top
of code-defined providers, a provider's DECLARATIVE definition becomes
JSON data the provider reads to register itself — vocabulary, attr
constraints, descriptions, and rule PROSE, all single-sourced. Root fix:
the declarative metamodel stops being hand-coded 5× and gated; it's
authored once and consistent by construction.

Key additions: the data/code boundary (file = declarative + rule prose;
imperative validation + codegen stay per-port code — no rules DSL); file
layering (global/core in spec/metamodel, language-specific overlays in
local port repos, overlay/last-writer-wins merge; a provider reads ≥1
file); registration reads+merges embedded files; registry-conformance
extended to verify embed-integrity + description coverage + core
identity; doc-gen + agent-context wiring unchanged; provider-by-provider
migration sequencing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…metamodel docs)

Adds the FR-033 row linking GH #23 and stamps the design spec with its
FR number.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implementation plan for FR-033. Phase 1 (TS foundation on the field
provider) is fully bite-sized + grounded in the real registry/provider
APIs (the registry ALREADY carries TypeDefinition.description +
AttrSchema.description — the manifest just excludes them, so this is
"source from a shared file + emit + gate + doc-gen," not "add the
slot"). Phases 2-4 (remaining TS providers, tiered doc-gen + agent-
context wiring, cross-port fan-out) are recipe-driven task groups that
repeat the proven Phase-1 recipe; each may expand into its own detailed
plan when reached. Data/code boundary held throughout: declarative facts
+ rule prose in JSON; factories, imperative validation, codegen stay
code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Folds in the brainstormed structural-constraint model: a single unified
`children` list per type/subtype (attrs are the type:"attr" entries;
structural children are field/identity/etc., distinguished by type),
uniform min/max cardinality (attr = required/optional, max always 1;
structural = full 0..N), a child-side additive `parents` list, purely
additive merge (provider union + extends inheritance, never narrowing),
and one contradiction pass (six checks: dangling ref, unsatisfiable
required child, min>max, closed-set clash, required-cycle, conflicting
attr redefinition). Adds Phase 1b plan tasks 7-9 (constraint fields,
additive merge, contradiction validator).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ation)

Plan Tasks 1+2 + the format half of Task 7. New provider-data.ts: the
declarative provider-definition format — ProviderDefinition / TypeDef /
ChildDef (the unified child requirement: attrs are type:"attr" entries
carrying value-type/default/allowedValues/isArray; structural children
are field/identity/... entries; subType accepts value|list|"*";
cardinality min/max; named) + FactoryMap. defineProviderFromData fans an
attr child-entry to an AttrSchema (required = min>=1) and a structural
entry to a ChildRule, passes parents/rules/example/whenToUse onto the
TypeDefinition; throws on missing factory + the attr/cardinality
validations (attr max!=1 unless isArray, list-subType attr, min<0,
max<min). registry.ts: ChildRule.childSubType widened to string|list +
optional min/max/named; AttrSchema/TypeDefinition gain optional
rules/example/whenToUse (+ TypeDefinition.parents). The factory stays
code — the data/code boundary.

13 new tests; metadata suite 1727/0; typecheck clean. Foundation only —
no provider/manifest changes yet (Tasks 4/5/8/9).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dator

Plan Tasks 8+9 (spec §3.1). constraint-merge.ts: mergeConstraints()
produces EffectiveConstraints per type.subType — horizontal provider
union (a child-side `parents` claim additively folds into the named
parent's effective children — the extensibility point) + vertical
extends inheritance (additive union of super's children+attrs; TS tracks
super as <type>.base today; conflicting redefs left for check #6). Never
subtracts. constraint-validate.ts: validateConstraints() runs the six
contradiction checks (dangling ref, unsatisfiable required child, bad
cardinality, closed-set clash, required-child cycle via colored DFS,
conflicting attr redefinition), each → ERR_INVALID_METAMODEL_CONSTRAINT
(new code, added to errors.ts + the shared ERROR-CODES.json allowlist).

Wired into bootstrap: composeRegistry(providers, {validate:true}) and
MetaDataLoader's default registry — so a contradictory provider set fails
at load. The library's own metamodel is contradiction-free (no check
loosened). 12 new tests; metadata suite 1739/0; full server suite green;
typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First real provider conversion under FR-033. The field provider's declarative
metamodel definition (per-subtype attr constraints + descriptions + rule prose)
is externalized to spec/metamodel/field.json as a ProviderDefinition and embedded
at build, replacing the hand-coded field-schema.ts composition.

- Authored spec/metamodel/field.json: faithfully externalizes commonFieldAttrs +
  the currency/enum special attrs (descriptions copied verbatim) and the 15-entry
  subtype list, with a one-line description per subtype + rules prose where a
  complex cross-cutting rule applies (currency wire-format, enum member pattern,
  object @storage). Attrs use the unified children shape (type:"attr" entries);
  per-subtype dataType matches MetaField's internal FIELD_DATA_TYPE map.
- Added scripts/generate-embedded-metamodel.ts (globs spec/metamodel/*.json,
  repo-root discovery, no hardcoded paths) emitting field-definition.embedded.ts
  (FIELD_DEFINITION typed object literal); gated by a deep-equal drift test.
- Rewired core-types.ts field loop to defineProviderFromData(FIELD_DEFINITION,
  FIELD_FACTORIES), post-assigning the unchanged fieldRules so registered
  childRules stay byte-identical (the JSON carries attrs only). Trimmed
  field-schema.ts to the still-shared normalizeAttr (used by object.value too).
- Made ChildDef.subType optional in provider-data.ts so a polymorphic attr
  (@default) can omit its value-type, matching AttrSchema's optional valueType.
- Added a completeness check asserting the composed registry registers, per
  subtype, exactly the pre-FR-033 attr name-set + valueType + required + dataType.

Manifest unchanged: fixtures/registry-conformance/expected-registry.json stays
byte-identical (the manifest excludes description/default/allowedValues/childRules;
attr name/valueType/isArray/required sets are preserved). 1758 metadata tests
pass (was 1739 + 19 new), workspace typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + coverage gate (TS)

ManifestType/ManifestAttr now carry description + optional rules/example/whenToUse,
and each ManifestType carries the structural constraint graph: children (from
childRules — childType/childSubType/childName + optional cardinality min/max/named,
canonically sorted) and optional parents (the FR-033 child-side placement claim,
sorted). description is required + non-empty on every type and attr; a coverage
gate (registry-conformance.test.ts) hard-fails CI on any empty/missing description
(mirrors ADR-0023). childSubType may be a string, "*", or a string[]; cardinality
keys are emitted ONLY when defined on the rule (max: null = unbounded emitted,
undefined omitted) — legacy wildcard rules fabricate no cardinality.

expected-registry.json regenerated — the diff is PURELY ADDITIVE (every pre-existing
field — type/subType, attr name/valueType/isArray/required — is byte-unchanged; only
new keys were added; +1167 lines). A committed regen script
(scripts/regen-expected-registry.ts, repo-root-discovery, no hardcoded paths) drives
Phase 2 regeneration. README + exclusions-file header updated: type/attr description
+ the constraint graph are now first-class manifest properties (the COMMON_ATTR_DUP
carve-out for the per-type attr NAMED "description" is unchanged — distinct mechanism).

Growing the canonical RED-flags the other four ports' (Java/C#/Python/Kotlin)
registry-conformance until Phase 4 — the intended, documented intermediate state,
same lifecycle as the FR-032 sweep. The TS reference is correctly advanced; the
ports reconcile later. TS reference: 1760/1760 metadata tests pass, typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Second provider conversion under FR-033, mirroring the field conversion
(a72f8bb). The attr type family's declarative metamodel definition is
externalized to spec/metamodel/attr.json as a ProviderDefinition and embedded
at build, replacing the hand-coded probe-and-register loop in core-types.ts.

- Authored spec/metamodel/attr.json: provider metaobjects-core-types, one
  TypeDef per attr subtype (9: base/string/int/long/double/boolean/class/
  properties/filter — stringarray stays unregistered) with a real one-line
  description (replacing the generic "Attribute of type X") and the per-subtype
  dataType probed off the subtype's MetaAttr class (base/string/class→string,
  int→int, long→long, double→double, boolean→boolean, properties/filter→object).
  No children — attrs are leaf value-type vocabulary (no childRules, no attrs).
- Generated core/attr/attr-definition.embedded.ts (ATTR_DEFINITION) via
  scripts/generate-embedded-metamodel.ts; gated by a deep-equal drift test
  (test/attr-definition-embed.test.ts).
- Rewired core-types.ts attr block to defineProviderFromData(ATTR_DEFINITION,
  ATTR_FACTORIES). ATTR_FACTORIES is a loop over ATTR_SUBTYPES constructing via
  attrClassFor(subType); each class computes its own dataType internally, so no
  setDataType. childRules are empty for attr, so NO post-assign is needed (unlike
  field, which carries the validator/view/attr/origin wildcards).
- Added a completeness check (test/attr-definition-completeness.test.ts)
  asserting, per subtype, empty attributes, empty childRules, and dataType ==
  the pre-FR-033 probe.

Regenerated fixtures/registry-conformance/expected-registry.json: the diff
touches ONLY the 9 attr.* type-level description fields (dataType is excluded
from the manifest; attr name/attrs/children sets are unchanged). The other four
ports (C#/Java/Kotlin/Python) go RED on registry-conformance until their Phase-4
reconciliation — the same documented intermediate state as the field conversion.
1772 metadata tests pass (was 1760 + 12 new), metadata typecheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(embedded)

Phase 2 provider conversion mirroring the landed field (a72f8bb) and attr
(e3e33c4) conversions. The validator type family's declarative definition
(6 subtypes — base/required/length/regex/numeric/array — vocabulary +
per-subtype attr constraints + real descriptions) is externalized to
spec/metamodel/validator.json, embedded at build into VALIDATOR_DEFINITION.

- spec/metamodel/validator.json: 6 TypeDefs with real subtype descriptions
  replacing the generic "Validator (X)"; @min/@max (int, optional) on
  base/length/numeric/array, +@pattern (string, optional) on regex, none on
  required. Attr descriptions copied verbatim from the old validator-schema.ts.
  No dataType (validators carry no typed value).
- core/validator/validator-definition.embedded.ts: generated embed + a
  deep-equal drift gate (test/validator-definition-embed.test.ts).
- core-types.ts: rewired the validator block to defineProviderFromData(
  VALIDATOR_DEFINITION, VALIDATOR_FACTORIES) with the subType→class factory
  reusing VALIDATOR_CLASS_MAP; the structural childRules (= [wildcard(attr)])
  are kept byte-identical by post-assigning the constant validatorRules array
  after lowering (the wildcard can't be a type:"attr" JSON entry). Removed the
  now-dead VALIDATOR_ATTRS_MAP import and deleted the fully-superseded
  validator-schema.ts.
- test/validator-definition-completeness.test.ts: per-subtype asserts the
  registered attr name-set + valueType + required + childRules == [wildcard(attr)]
  + no dataType, hardcoded from the pre-conversion VALIDATOR_ATTRS_MAP.
- expected-registry.json regenerated: the diff touches ONLY the 6 validator.*
  descriptions; the children/attrs blocks (the attr wildcard + @min/@max/@pattern)
  were already in the Task-5 canonical and stay byte-identical.

metadata suite 1788 pass / 0 fail; typecheck + build clean. Other ports go
RED until Phase 4 (expected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mbedded)

Phase 2 provider conversion mirroring the landed validator (58c0eda), field
(a72f8bb), and attr (e3e33c4) conversions. The identity type family's
declarative definition (3 subtypes — primary/secondary/reference, NO base —
vocabulary + per-subtype attr constraints + real descriptions) is externalized
to spec/metamodel/identity.json, embedded at build into IDENTITY_DEFINITION.

- spec/metamodel/identity.json: 3 TypeDefs with real subtype descriptions
  replacing the generic "Identity (X)". All three carry the required, isArray
  @fields (min:1/max:1/isArray:true); primary adds @generation (allowedValues
  increment/uuid/assigned), secondary adds @unique (boolean), reference adds
  @references (required) + @Enforce (boolean). Attr descriptions copied verbatim
  from the old identity-schema.ts. No dataType (identities carry no typed value).
- core/identity/identity-definition.embedded.ts: generated embed + a deep-equal
  drift gate (test/identity-definition-embed.test.ts).
- core-types.ts: rewired the identity block to defineProviderFromData(
  IDENTITY_DEFINITION, IDENTITY_FACTORIES) with the subType->class factory
  reusing IDENTITY_CLASS_MAP (primary->MetaPrimaryIdentity,
  secondary->MetaSecondaryIdentity, reference->MetaReferenceIdentity,
  default->MetaIdentity). The structural childRules (= [wildcard(attr)]) are kept
  byte-identical by post-assigning the constant identityRules array after
  lowering (the wildcard can't be a type:"attr" JSON entry). Removed the now-dead
  identityFieldsAttr/IDENTITY_ATTRS_MAP imports and deleted the fully-superseded
  identity-schema.ts (core-types.ts was its only importer).
- test/identity-definition-completeness.test.ts: per-subtype asserts the
  registered attr name-set + valueType + isArray + required + allowedValues +
  childRules == [wildcard(attr)] + no dataType, hardcoded from the pre-conversion
  IDENTITY_ATTRS_MAP.
- expected-registry.json regenerated: the diff touches ONLY the 3 identity.*
  descriptions; the children/attrs blocks (the attr wildcard + @fields/
  @generation/@unique/@references/@Enforce) were already in the Task-5 canonical
  and stay byte-identical.

metadata suite 1798 pass / 0 fail (baseline 1788 + 10 new); typecheck + build
clean. Other ports go RED until Phase 4 (expected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
….json (embedded)

Phase 2 provider conversion mirroring the landed validator (58c0eda), identity
(3eb610d), field (a72f8bb), and attr (e3e33c4) conversions. The relationship
type family's declarative definition (4 subtypes — base/association/aggregation/
composition, all backed by the single MetaRelationship node class with NO
subType->class dispatch — vocabulary + the 7 shared attr constraints + real
descriptions + the complex M:N rules prose) is externalized to
spec/metamodel/relationship.json, embedded at build into RELATIONSHIP_DEFINITION.

- spec/metamodel/relationship.json: 4 TypeDefs with real subtype descriptions
  replacing the generic "Relationship (X)". All four carry the SAME 7 attrs,
  copied VERBATIM from the old relationship-schema.ts (relationshipAttrs):
  @Cardinality (string, open — no allowedValues), @objectref, @through,
  @sourceRefField (string, optional), @Symmetric (boolean), @onDelete / @onUpdate
  (string, allowedValues cascade/set-null/restrict/no-action). Attr descriptions
  copied verbatim. No dataType (relationships carry no typed value). Each TypeDef
  carries a top-level `rules` string capturing the genuinely-complex M:N model
  (@through junction MUST declare two identity.reference children; FK fields are
  DERIVED from those references, never restated; @sourceRefField = directed
  self-join, @Symmetric = undirected self-join, mutually exclusive) plus the
  per-subtype lifecycle semantics (composition cascade / aggregation set-null /
  association restrict) — documentation prose; enforcement stays in code.
- core/relationship/relationship-definition.embedded.ts: generated embed + a
  deep-equal drift gate (test/relationship-definition-embed.test.ts).
- core-types.ts: rewired the relationship block to defineProviderFromData(
  RELATIONSHIP_DEFINITION, RELATIONSHIP_FACTORIES) with the subType->class factory
  built over RELATIONSHIP_SUBTYPES mapping every subtype to MetaRelationship. The
  structural childRules (= [wildcard(attr)]) are kept byte-identical by
  post-assigning the constant relationshipRules array after lowering (the wildcard
  can't be a type:"attr" JSON entry). Removed the now-dead relationshipAttrs
  import and deleted the fully-superseded relationship-schema.ts (core-types.ts was
  its only importer; relationship-constants.ts kept).
- test/relationship-definition-completeness.test.ts: per-subtype asserts the
  registered attr name-set + valueType + required + allowedValues match the
  pre-conversion relationshipAttrs, childRules == [wildcard(attr)], no dataType.
- expected-registry.json regenerated: the diff touches ONLY the 4 relationship.*
  descriptions AND adds `rules` to each; the children/attrs blocks stay
  byte-identical to the Task-5 canonical.

metadata suite 1810 pass / 0 fail (baseline 1798 + 12 new); typecheck + build
clean. Other ports go RED until Phase 4 (expected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ded)

4 subtypes (base/entity/value/projection): verbatim @discriminator/@discriminatorValue
on all + @normalize on value, real descriptions, and ADR-0028 taxonomy rules prose
(entity owns data; value pure shape no identity/source; projection derived read-only,
omits relationship+template). Per-subtype structural childRules are post-assigned in
code (entity adds template; projection drops relationship+template; base/value carry
relationship, no template) — the completeness test pins both attrs and childRules per
subtype, and the regenerated canonical's object.* children blocks are byte-identical
(only descriptions changed + rules added). Frees and deletes the last consumer of
core/field/field-schema.ts. RED-flags non-TS ports' registry-conformance until Phase 4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ded)

Generalizes scripts/generate-embedded-metamodel.ts (concept→dir map covering
persistence/presentation/template; computed relative provider-data import) — the 6
existing embeds regenerate byte-identical. Converts origin (4 subtypes) via
defineProviderFromData + post-assigned childRules; completeness check; canonical
regenerated (origin.* descriptions only). RED-flags non-TS ports until Phase 4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ded)

Phase 2 provider conversion mirroring the landed origin (bed24df) and
validator (58c0eda) conversions. Converts ONLY the CORE source registration
(the bare source shells — base/rdb — that coreTypesProvider registers with NO
own attrs) to read from the externalized spec/metamodel/source.json, embedded
at build into SOURCE_DEFINITION.

- spec/metamodel/source.json: 2 TypeDefs (base, rdb) with real descriptions
  replacing the generic "Source (X)" + ADR-0007 rules prose on rdb (source
  declares where an object's data lives; rdb is the relational paradigm subtype;
  physical name is @table not the structural name; read-only-ness is derived
  from @kind; multi-source via @ROLE with exactly one primary per object).
  children == EMPTY (no own attrs) and no dataType — the
  @table/@kind/@role/@schema/@parameterRef attrs on source.rdb are NOT here;
  they are contributed by a SEPARATE provider (dbProvider, persistence/db) via
  registry.extend, which is UNTOUCHED by this conversion.
- persistence/source/source-definition.embedded.ts: generated embed + a
  deep-equal drift gate (test/source-definition-embed.test.ts). The 7 existing
  embeds regenerate byte-identical.
- core-types.ts: rewired the source block to defineProviderFromData(
  SOURCE_DEFINITION, SOURCE_FACTORIES) over SOURCE_SUBTYPES (all -> MetaSource);
  the structural childRules (= [wildcard(attr)]) are kept byte-identical by
  post-assigning the constant sourceRules array after lowering. No dead imports
  to remove (the old source block used no schema import).
- test/source-definition-completeness.test.ts: composes with CORE ONLY
  (composeRegistry([coreTypesProvider])) so the dbProvider extend does not add
  attrs, and asserts per subtype: own attributes == [], childRules ==
  [wildcard(attr)], no dataType.
- expected-registry.json regenerated: the composed canonical includes
  dbProvider, so source.rdb's attrs block (table/view/kind/role/schema/...)
  stays byte-identical — the diff touches ONLY the source.base + source.rdb
  descriptions (+ the rdb rules prose).

metadata suite 1846 pass / 0 fail; typecheck + build clean. Other ports go RED
until Phase 4 (expected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ded)

Phase 2 provider conversion mirroring the landed validator (58c0eda) and
origin (bed24df) conversions. The layout type family's declarative definition
(2 subtypes — base/dataGrid — vocabulary + the 6 dataGrid attr constraints +
real descriptions) is externalized to spec/metamodel/layout.json, embedded at
build into LAYOUT_DEFINITION.

- spec/metamodel/layout.json: 2 TypeDefs with real subtype descriptions
  replacing the generic "Layout (X)" (base = abstract object-level UI surface;
  dataGrid = a metadata-driven data grid). The 6 dataGrid attrs are copied
  verbatim from the old layout-schema.ts (dataGridLayoutAttrs): @pageSize (int),
  @defaultSortField (string), @defaultSortOrder (string, allowedValues
  asc/desc), @filterable (boolean), @filter (valueType filter), @columns
  (string, isArray:true) — all optional. base carries no attrs. No dataType.
- presentation/layout/layout-definition.embedded.ts: generated embed + a
  deep-equal drift gate (test/layout-definition-embed.test.ts).
- core-types.ts: rewired the layout block to defineProviderFromData(
  LAYOUT_DEFINITION, LAYOUT_FACTORIES); every subtype maps to the single
  MetaLayout class (layouts carry no per-subtype behavior). The structural
  childRules (= [wildcard(attr)]) are kept byte-identical to the pre-FR-033
  registration by post-assigning them after lowering (the wildcard can't be a
  type:"attr" JSON entry). Deleted the now-dead layout-schema.ts; only
  core-types.ts imported dataGridLayoutAttrs.
- test/layout-definition-completeness.test.ts: per-subtype asserts the
  registered attr name-set + valueType (incl. @filter→"filter") + isArray (incl.
  @columns→true) + required + allowedValues (asc/desc) match the pre-conversion
  dataGridLayoutAttrs; childRules == [wildcard(attr)]; no dataType.
- expected-registry.json regenerated: the diff touches ONLY the 2 layout.*
  descriptions; the dataGrid attrs/children blocks were already in the Task-5
  canonical and stay byte-identical.

metadata suite 1854 pass / 0 fail (baseline 1846 + 8 new); typecheck clean. The
8 existing embeds regenerated byte-identical. Other ports go RED until Phase 4
(expected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 2 provider conversion mirroring the landed field (a72f8bb), validator
(58c0eda) and layout (6fb2e11) conversions. The view type family's declarative
definition (13 subtypes — base/text/textarea/date/month/hotlink/dropdown/radio/
checkbox/number/password/hidden/web/currency — vocabulary + real per-subtype
descriptions + the single @Locale attr on view.currency) is externalized to
spec/metamodel/view.json, embedded at build into VIEW_DEFINITION.

- spec/metamodel/view.json: 13 TypeDefs with real one-line descriptions
  replacing the generic "View (X)" — including the 11 generic presentation
  controls that are manifest-excluded (PRESENTATION_ONLY) but still REGISTERED,
  so every subtype carries a faithful description for the embed + docs. Only
  view.currency carries an attr: @Locale (string, optional, default "en-US",
  description copied verbatim from the old view-schema.ts); the other 12 have no
  children. No dataType (single MetaView class, no typed value).
- presentation/view/view-definition.embedded.ts: generated embed + a deep-equal
  drift gate (test/view-definition-embed.test.ts). The 9 existing embeds
  regenerate byte-identical.
- core-types.ts: rewired the view block to defineProviderFromData(
  VIEW_DEFINITION, VIEW_FACTORIES); every subtype maps to the single MetaView
  class (views carry no per-subtype behavior). The structural childRules
  ([wildcard(attr)]) are kept byte-identical to the pre-FR-033 registration by
  post-assigning them after lowering (the wildcard is not modeled in the JSON,
  which carries attrs only). Removed the now-dead currencyViewAttrs import +
  the VIEW_SUBTYPE_CURRENCY import (used only by the old loop) and deleted the
  fully-superseded view-schema.ts. view-constants.ts kept (VIEW_SUBTYPE_CURRENCY
  still consumed by registry-manifest-exclusions / codegen / tests).
- test/view-definition-completeness.test.ts: asserts all 13 subtypes registered;
  view.currency has exactly the @Locale attr (valueType "string", required false,
  default "en-US"); all 12 other subtypes have own attributes == []; childRules
  == [wildcard(attr)] for every subtype; no dataType.
- expected-registry.json regenerated: the diff touches ONLY the view.base +
  view.currency descriptions — the 11 generic controls are manifest-excluded so
  their description change is invisible to the canonical; the view.currency
  @Locale attr block stays byte-identical.

metadata suite 1886 pass / 0 fail (was 1854 + 32 new); typecheck + build clean.
Other ports go RED until Phase 4 (expected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mbedded)

Convert coreTypesProvider's template registration to read its declarative
definition from spec/metamodel/template.json (embedded at build into
TEMPLATE_DEFINITION), lowered via defineProviderFromData. The most attr-heavy
provider: 4 subtypes (base/prompt/output/toolcall) with all ~16 attrs
externalized verbatim incl. allowedValues + defaults + required flags —
@Format (default "text", 7 formats), @PromptStyle (default "guide", 3 styles),
@kind (default "document", 2 kinds), required @payloadRef (prompt/output) and
required @toolname + @payloadRef (toolcall). Real subtype descriptions replace
the old "Template (X)" placeholders, plus FR-004/ADR-0011 rules prose capturing
the document-vs-email @textRef/@subjectRef/@htmlBodyRef presence rule and the
toolcall no-renderable-text rationale.

The factory stays code via TEMPLATE_FACTORIES (every subType -> MetaTemplate);
structural childRules post-assigned [wildcard(attr)] byte-identically. The
separate templateProvider (which EXTENDS every field subtype with @xmlText) is
UNTOUCHED — this conversion covers only the core template registration. The
depth-1 embed import is ../provider-data.js and resolves.

A completeness check pins every attr (name-set / valueType / isArray / required
/ default / allowedValues + childRules + no dataType) against the pre-FR-033
TEMPLATE_ATTRS_MAP; a drift gate deep-equals the embed to the JSON. Canonical
registry regenerated: the ONLY diff is template.* descriptions + new rules prose
(attrs/children blocks byte-unchanged). Deleted the now-unused template-schema.ts
(only importer was core-types.ts). RED-flags non-TS ports until Phase 4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on.json (embedded)

Adds optional commonAttrs to ProviderDefinition + defineCommonAttrsFromData (the
common-attr analog of defineProviderFromData). docProvider now registers the 8
universal doc common attrs from data instead of hand-coded commonDocAttrs.
Canonical UNCHANGED (verbatim externalization — the commonAttrs block already
carried these descriptions since Task 5), so no new cross-port RED. Completes the
Phase-2 TS core-provider conversions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r split

Revises the FR-033 approach from the loose model (attrs lumped in core,
code-post-assigned wildcard childRules, bespoke documentation path) to the
pre-6.0-Java strict model: per-subtype fail-closed child rules enforced in
validate(), attrs scattered across 5 concern providers (core / db / documentation
/ prompt / ui), only documentation universal. Records the attr-ownership map and
the S0..S5 implementation sequence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on attrs + structural placement enforcement

Unifies the provider apply path (base-composition + a universal *.* entry for common
attrs), deletes the bespoke commonAttrs helper, reworks documentation to the *.*
universal model, and adds ERR_CHILD_NOT_ALLOWED structural-child enforcement in
validate() (a no-op under today's wildcard childRules — the rail strict per-subtype
rules will use in S1). Canonical byte-identical; full metadata suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ui/prompt/db)

Creates metaobjects-ui (filter/sort field attrs), expands templateProvider into
metaobjects-prompt (adds @example/@Instruction + enum extract attrs + the
object.value @normalize default to its existing @xmlText), grows metaobjects-db
(@storage/@autoset), and drops all of these from core field.json. Pure ownership
move at current subtype scope — canonical byte-identical. (Strict per-subtype
re-scoping is step B.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
field.json restructured to the strict model: field.base holds the universal core
attrs (@required/@readOnly/@default/@unique) + the open validator/view/origin child
rules; each subtype extendsBase and adds only its own (@maxlength→string,
@precision/@scale→decimal, @Currency→currency, @objectref→object, @values/@provided
→enum). The "any attr" wildcard child rule is dropped (strict/fail-closed); childRules
no longer post-assigned in code. db @storage→object, @autoset→temporal. Misplaced
attrs now rejected (ERR_UNKNOWN_ATTR). Canonical field.* re-scoped; corpus fixtures
fixed: auto-set-on-create / auto-set-on-update / auto-set-on-create-and-update
(@autoset field.string → field.timestamp). Non-TS ports RED until S5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…roviders

validator/view/layout/source/origin/identity/relationship now register empty
childRules (named attrs only, no any-attr catch-all); metadata.root drops its
attr wildcard but keeps its open structural child rules. Strict/fail-closed:
a structural child under an attr-only type is now ERR_CHILD_NOT_ALLOWED, a
misplaced attr stays ERR_UNKNOWN_ATTR. Canonical drops the (attr,*,*) childRule
from those types; attrs unchanged. Corpus fixtures fixed: none.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…core provider)

object.json restructured to the strict model via extendsBase: object.base holds the
intersection child rules (field/identity/validator/layout/source); value adds
relationship; entity adds relationship+template; projection inherits base only. The
any-attr wildcard is dropped from all four. @discriminator/@discriminatorValue scoped
to object.entity only (FR-014 TPH is entity-inheritance). childRules no longer
post-assigned in code. Canonical object.* re-scoped; corpus fixtures fixed: none
(no fixture placed a discriminator on a non-entity object or a relationship/template
under projection). Completes the strict re-scope of all core providers. Non-TS ports
RED until S5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ads spec/metamodel/db.json

Adds ProviderDefinition.extends ({type, subType: value|list|"*", children}) applied via
registry.extend in the unified apply path, so concern (extend) providers are data-driven
like the core types. Converts dbProvider to read spec/metamodel/db.json (field @column/
@db.indexed/@dbColumnType + @storage→object + @autoset→temporal + source.rdb attrs).
Canonical byte-identical. (ui/prompt follow in S1.5-B; type-attr re-home in S2.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…prompt}.json

uiProvider and promptProvider now register their field/object.value attr extensions
from data (spec/metamodel/ui.json, prompt.json) via the S1.5-A extends mechanism,
replacing the hand-coded registry.extend arrays. All three concern providers (db/ui/
prompt) are now data-driven. Canonical byte-identical. (Type-attr re-home is S2.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pt; template strict

view.currency @Locale + layout.dataGrid attrs move to the ui provider; template.*
type attrs (@payloadRef/@textRef/@format/@kind/...) move to the prompt provider — all
via data-driven extends. template's any-attr wildcard childRule is dropped (strict
completion; toolcall vendor attrs stay extensible via consumer registry.extend).
Composed attrs unchanged (canonical attrs byte-identical); only template.* children
drop the (attr,*,*) wildcard. Completes the 5-provider concern ownership. Corpus
fixtures fixed: extends-view-triple-nest (+metaobjects-ui), flattened-kitchen-sink
(+metaobjects-prompt). Non-TS ports RED until S5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dmealing and others added 20 commits June 14, 2026 10:51
…er index)

Generates LLM-readable docs FOR THE METAMODEL from the strict registry: metamodel/
INDEX.md (every type.subType + one-liner + link), types/<type>.md (each subtype's
full composed allowed attrs — provider-tagged — + child rules + cardinality), and
providers.md (the 5-concern ownership index). New `meta docs --metamodel` surface
(needs no user metadata); byte-gated docs fixture. Authoring-skill wiring is a
follow-on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…EX/type pages

providers.md's core "Owns" list now applies isExcludedTypeSubType, so the 11
manifest-excluded generic view.* controls no longer appear there (they're absent
from INDEX/type pages). All three doc surfaces now share one vocabulary. Fixture
regenerated; consistency gated by a new assertion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cipe

Measured the non-TS gap (Java RED: emits v1 manifest only; golden now carries
description/rules/children/parents + strict per-subtype scoping). Records the two
load-bearing facts (manifest is provider-agnostic → 5-split is internal Tier-3;
descriptions must be byte-identical → ports read the shared spec/metamodel/*.json)
+ the per-port recipe (embed JSON → data-driven register → grow emitter → byte-match
→ enforce) and Java→C#→Python sequencing (Kotlin via JVM).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… slots

Grows the Java RegistryManifest emitter to the TS reference shape (per-type
description/rules/example/whenToUse + per-attr description + the children
constraint graph [any-attr wildcard dropped] + parents), and adds the optional
model slots (ChildRequirement doc description; TypeDefinition rules/example/
whenToUse/parents) the JSON reader will populate in sub-step B. Emitter structure
matches registry-manifest.ts byte layout; RegistryManifestConformanceTest stays
RED until S-B sources descriptions + the strict graph from spec/metamodel/*.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…escriptions

Bundles the 15 shared spec/metamodel/*.json into the metadata module (byte-gated)
and adds a reader that sources every type/attr/common-attr description (+ rules/
example/whenToUse) from them onto the Java registry — single-sourced, byte-identical
to TS, never hand-copied. The manifest's description fields now match the golden;
RegistryManifestConformanceTest stays RED pending the strict children graph +
cardinality + per-subtype attr scoping reconciliation (sub-step B2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rom JSON

Adds cardinality (min/max/named) to ChildRequirement and extends the pre-seal apply
hook to replace each type's structural child requirements with the strict graph from
spec/metamodel/*.json (extendsBase-composed, any-attr/extra wildcards dropped). The
manifest children blocks now byte-match the golden; RegistryManifestConformanceTest
stays RED pending per-subtype attr scoping + the ui/prompt providers + metadata.root
(B2b).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… registry-conformance GREEN

Prunes each type.subType's logical attr requirements to the strict per-subtype set
sourced from spec/metamodel/*.json (extendsBase + extends composed), so the manifest
attrs blocks byte-match the golden and the loader rejects misplaced attrs
(ERR_UNKNOWN_ATTR). Fixes metadata.root (the hand-coded root wrapper) to the strict
4 structural children + "Root metadata document". RegistryManifestConformanceTest is
now GREEN — the Java metadata registry byte-matches the cross-port canonical. Corpus
fixtures fixed: none (no fixture placed a now-disallowed attr; the loader-test
root-attr capability is preserved via the retained any-attr wildcard, which is not a
structural child so it does not affect the manifest). Also fixed pre-existing NUL
bytes that had been written in place of space separators in two sort-key string
literals (SpecMetamodelReader.structKey, RegistryManifest child sort key).

The strict allow-list per (type, subType) is composed in SpecMetamodelReader
.strictAttrNames exactly as TS composes it: the subtype's own types[].children attrs,
plus <type>.base's attrs when extendsBase:true, plus every extends block (db/ui/prompt
JSON) whose matcher targets the subtype (exact / list / "*"). A new pre-seal apply pass
drops every INCLUDED attr requirement (direct + inherited) not in that set, leaving the
classifyPerTypeAttr carve-outs (isArray/isAbstract/extends/implements/isInterface/
object/objectAdapter/description) registered.

(The 5 metaobjects-ui/prompt-provider ConformanceTest failures are a separate gate —
next sub-step.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…el slots

Grows the Python registry_manifest emitter to the TS reference shape (per-type
description/rules/example/whenToUse + per-attr description + the children
constraint graph + parents) and adds the optional model slots (AttrSchema.description;
ChildRule.child_name/min/max/max_is_null/named; TypeDefinition.description/rules/
example/whenToUse/parents) the JSON reader fills in S-B1. test_registry_conformance
stays RED until S-B sources descriptions + the strict graph from spec/metamodel/*.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… descriptions

Embeds the 15 shared spec/metamodel/*.json as Python package data (byte-gated) +
adds a reader sourcing every type/attr/common description (+rules/example/whenToUse)
from them onto the Python registry pre-seal — single-sourced, byte-identical to TS,
never hand-copied. The manifest's description fields now match the golden;
test_registry_conformance stays RED pending the strict children graph + cardinality
+ per-subtype attr scoping (S-B2a/B2b).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oot → registry-conformance GREEN

Extends the pre-seal apply hook to replace each type's structural child_rules with the
strict graph + cardinality from spec/metamodel/*.json (extendsBase-composed) and prune
its logical attrs to the strict per-subtype allow-list (loader now rejects misplaced
attrs); fixes metadata.root (hand-coded root wrapper); adds the ERR_CHILD_NOT_ALLOWED/
ERR_INVALID_METAMODEL_CONSTRAINT error codes. test_registry_conformance is now GREEN —
the Python metadata registry byte-matches the cross-port canonical. Corpus fixtures
fixed: none. (The 5 metaobjects-ui/prompt-provider conformance failures are a
separate provider-composition gate — follow-on.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tch TS); 5 conformance fixtures green

Adds data-driven metaobjects-ui + metaobjects-prompt providers (reading the embedded
ui.json/prompt.json) and re-homes @filterable/@sortable/@sortableDefaultOrder + view/
layout type attrs (ui) and @example/@instruction/@xmlText/enum-extract + template type
attrs (prompt) out of core into them, matching the TS provider split — so the 5
conformance fixtures that declare those provider ids compose. registry-conformance
stays GREEN (manifest is provider-agnostic; composed attr set unchanged); no
double-registration. metaobjects-template kept as a back-compat alias.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pt providers (match TS/Python)

Brings Java in line with TS + Python: ui/prompt metadata config now lives in dedicated
data-driven metaobjects-ui + metaobjects-prompt providers (reading the embedded ui.json/
prompt.json extends), NOT in core. Renames TemplateTypesMetaDataProvider →
metaobjects-prompt (absorbs @xmlText; metaobjects-template kept as a back-compat alias),
adds metaobjects-ui. Removes the re-homed attrs from core (no double-registration).
registry-conformance + Kotlin stay GREEN (manifest is provider-agnostic); the 5
metaobjects-ui/prompt ConformanceTest fixtures now pass. All three ports now share the
same provider split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lots

Grows the C# RegistryManifest emitter to the TS reference shape (per-type description/
rules/example/whenToUse + per-attr description + the children constraint graph +
parents) and adds the optional model slots (ChildRule Min/Max/MaxIsNull/Named;
TypeDefinition Rules/Example/WhenToUse/Parents; AttrSchema Rules/Example/WhenToUse —
Description already existed) the JSON reader fills in S-B1. RegistryManifestConformance
stays RED until S-B sources descriptions + the strict graph from spec/metamodel/*.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…criptions

Embeds the 15 shared spec/metamodel/*.json as assembly resources (byte-gated) + adds a
reader sourcing every type/attr/common description (+rules/example/whenToUse) from them
onto the C# registry pre-seal — single-sourced, byte-identical to TS, never hand-copied
(overriding the providers' placeholder descriptions). The manifest's description fields
now match the golden; RegistryManifestConformance stays RED pending the strict children
graph + cardinality + per-subtype attr scoping (S-B2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…→ registry-conformance GREEN

Extends the pre-seal apply hook to replace each type's structural child rules with the
strict graph + cardinality from spec/metamodel/*.json (extendsBase-composed) and prune
its logical attrs to the strict per-subtype allow-list (loader now rejects misplaced
attrs); fixes metadata.root (hand-coded root wrapper); adds the missing error codes if
gated. RegistryManifestConformance is now GREEN — the C# metadata registry byte-matches
the cross-port canonical. Corpus fixtures fixed: none. (The 5 metaobjects-ui/
prompt ConformanceTests are a separate provider-composition gate — next step.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… providers (match TS/Java/Python)

Completes the cross-port ui/prompt re-home: C# ui/prompt metadata config now lives in
dedicated data-driven metaobjects-ui + metaobjects-prompt providers (reading the embedded
ui.json/prompt.json extends), NOT in core. Renames TemplateProvider → metaobjects-prompt
(absorbs @xmlText; metaobjects-template kept as a back-compat alias), adds metaobjects-ui.
Removes the re-homed attrs from core (no double-registration). registry-conformance stays
GREEN (manifest provider-agnostic); the 5 metaobjects-ui/prompt ConformanceTests now pass.
All 5 ported engines now share the same provider split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pesProvider

@xmlText is now registered by PromptMetaDataProvider (the ui/prompt re-home renamed
TemplateProvider → metaobjects-prompt). Comment-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pins that a misplaced structural child is rejected at strict load: a
relationship.association under object.projection (projection's strict child set
omits relationship) and a field under an attr-only validator.required (empty
strict child set). Java already enforces this eagerly at MetaData.addChild via
MetaDataRegistry.acceptsChild against the strict child graph (S-B2a), throwing
ERR_CHILD_NOT_ALLOWED — this test pins it against regression. Mirrors the TS
child-placement-enforcement test. Additive (test only); registry-conformance
unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Verifies + pins that a misplaced structural child is rejected at strict load (a
relationship under object.projection; a field under an attr-only validator.required).
Python's S-B2a strict child_rules were registered but the loader's attr-schema pass
did NOT consult them for STRUCTURAL placement (only ERR_UNKNOWN_ATTR for misplaced
attrs) — the two cases loaded clean. Wires the ERR_CHILD_NOT_ALLOWED check into
loader/validation_passes.py (_validate_attr_schema Check 0b): a structural child
(own_children — not an @-attr) not admitted by the parent's registered child_rules
now raises under strict, mirroring TS attr-schema-validate.ts (shared wildcard /
subtype-list match; lax mode stays a no-op; unregistered parent skipped).
registry-conformance stays GREEN. Corpus fixtures fixed: none.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
C# had the gap (strict child rules registered but the loader didn't check structural
placement). Wires the ERR_CHILD_NOT_ALLOWED check into load validation — a structural
child not admitted by the parent's strict child rules now raises (mirroring TS +
Python's Check 0b + Java's acceptsChild), and adds ERR_CHILD_NOT_ALLOWED to the
ErrorCode enum. Gating test pins the two cases (relationship under object.projection;
field under attr-only validator.required) + a lax no-op. registry-conformance stays
GREEN; full project 0 failures. Corpus fixtures fixed: none. Completes
structural-enforcement parity across all 5 ports.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dmealing
dmealing merged commit 01a5f43 into main Jun 15, 2026
29 checks passed
@dmealing
dmealing deleted the fr-033-strict-constraint-model branch June 15, 2026 01:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant