Skip to content

fix: ADR-0039 Phase-2 — resolve validation-layer + runtime own-reads the independent review found#140

Merged
dmealing merged 5 commits into
mainfrom
feat/own-phase2fix
Jul 1, 2026
Merged

fix: ADR-0039 Phase-2 — resolve validation-layer + runtime own-reads the independent review found#140
dmealing merged 5 commits into
mainfrom
feat/own-phase2fix

Conversation

@dmealing

@dmealing dmealing commented Jul 1, 2026

Copy link
Copy Markdown
Member

The independent post-sweep review (Phase 2) confirmed TS/JVM/C# codegen clean, but caught a straggler class in the VALIDATION layer + one runtime path that the sweep missed: own-only reads where the TS reference resolves, dropping extends-inherited values.

Genuine bugs fixed (each with a regression test that fails pre-fix): M:N junction-reference validation counted own children, so a junction inheriting its two identity.reference children via extends counted 0 → false ERR_INVALID_RELATIONSHIP (all 3 non-TS ports); Python runtime n2m_resolver iterated own_children, dropping an inherited M:N relationship at runtime. Plus consistency-aligned to the TS oracle: dataGrid @defaultSortField/@filter, relationship @objectRef/@through/@Cardinality, identity @fields/@generation, source @parameterRef, registered-validation reference-attr, and the template-validation cluster (TS resolved; JVM+Python read own — same template question, validation layer). Left genuinely-own: origin.* (never inherits), @Discriminator, @dbColumnType, required-attr presence, identity-passthrough author-disagreement checks.

Python b6013c2 · C# 327cce8 · JVM a0fa1a9. TS needed no change (it is the oracle). Zero golden changes; per-port suites + Testcontainers green.

🤖 Generated with Claude Code

claude added 5 commits July 1, 2026 00:24
…ted values → resolving

An independent review found 11 Python sites reading OWN attrs/children where the
TS reference RESOLVES (own + inherited via extends), each carrying a comment that
FALSELY claimed "TS uses ownAttr". Every such site silently drops a value
inherited from an abstract base. Flip each to the resolving accessor
(get_meta_attr / children) to match the TS oracle exactly, and correct the false
comments.

Sites (Python → TS oracle, all RESOLVING in TS):
- runtime/n2m_resolver.py: own_children() → children() (n2m-resolver.ts:63).
  MOST SEVERE — an entity inheriting an M:N relationship via extends dropped it
  at runtime.
- loader/validation_passes.py: @objectref via-path + single-hop inference
  (:522, :709); validateRelationships M:N attrs + root.children() (:1313-1324);
  @defaultSortField (:120); @filter (:1260); identity @fields (:291); and the
  full template-validation cluster — @payloadRef/@textRef/@kind/@subjectRef/
  @htmlBodyRef/@requiredSlots + subtype-only-attrs + root.children()
  (:169-253; templates CAN extend).
- loader/registered_validation.py: reference-attr value read (:66); recursion
  stays own.
- loader/validate_source_parameter_ref.py: @parameterRef, the param VO's fields,
  and the referenced entity's field (:76, :122, :139); source/origin reads stay
  own.
- loader/validate_field_readonly.py: identity @generation / @fields (:142,:144).

Adds a Python unit test proving the runtime n2m fix: an entity inheriting an M:N
relationship from an abstract base resolves the relation (fails pre-fix at
own_children, passes at children). Full suite green (1287 passed) except the two
pre-existing test_cli_staleness_nudge failures (agent-docs command string,
unrelated). No golden/fixture changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
…9 TS reference

The C# validation layer read several inheritable attrs via OwnAttr while the TS
oracle (server/typescript/packages/metadata) reads them RESOLVING — a concrete
node can pick the attr up from an abstract base via `extends`, so own-only reads
mis-validate inherited declarations. This flips every such site to the resolving
`Attr()` / `Children()` accessor, matching TS 1:1, and leaves the deliberate own
reads (origin.* which never inherits, @default declared-here coercibility,
declaration-structure OwnChildren tree walks, @discriminator/@discriminatorValue
root-vs-subtype semantics, @readonly downgrade own-vs-super comparison,
physical-name-alias per-source constraint) untouched.

Sites flipped (each vs its TS validation-passes.ts / validation-registry.ts /
validate-source-parameter.ts / validate-field-readonly.ts counterpart):
  - ValidateDataGridSortFields  @defaultSortField                 (TS:120)
  - ValidateFilterableHasIndex  identity @fields, @filterable, @db.indexed (TS:291,301,304)
  - ValidateFilterableHasSupportedOps  @filterable                (TS:332)
  - ValidateViaPath / InferViaSingleHop  relationship @objectref  (TS:522,709)
  - ValidateDataGridFilterValues  @filterable, @filter            (TS:1252,1260)
  - ValidateRelationships  @through/@sourceRefField/@symmetric/@cardinality/@objectref (TS:1320-1324)
  - JunctionReferenceFkFields / CountJunctionReferences → effective ReferenceIdentities() (TS:1293-1294)
  - ValidateTemplatePayloadRefs  @kind/@textRef/@subjectRef/@htmlBodyRef/@payloadRef/@requiredSlots (TS:169-253)
  - PrimaryAssignedFieldNames  identity @generation               (TS:142)
  - ValidateSourceParameterRef  @parameterRef, param fields, referenced field (TS:76,122,139)
  - RegisteredValidation.Walk  reference @attr (@objectRef/@through) (validation-registry.ts:66)

The M:N RUNTIME resolver (M2MResolver + M2MDerivation) was already correct — it
resolves via Relationships()/Fields()/ReferenceIdentities()/Children() and the
resolving MetaRelationship/MetaIdentity attr getters. But the M:N VALIDATOR read
junction references via OwnChildren, so a junction inheriting its two
identity.reference children via extends was mis-counted (found 0 → spurious
ERR_INVALID_RELATIONSHIP), disagreeing with the runtime derivation. Fixed +
gated by a new test.

Corrects the misleading "matches TS 1:1 via ownAttr" class doc-comments on
ValidationPasses and RegisteredValidation (the claim was false at these sites).

New tests (ValidationTests): M2N_junction_references_inherited_via_extends_are_counted
(fails against pre-fix source — genuine regression guard) +
M2N_relationship_inherited_via_extends_validates_cleanly (coherence guard).

MetaObjects.Conformance.Tests 676/676 green; Codegen.Tests 258 pass /1 skip with
one PRE-EXISTING, unrelated failure (ApiDocsCrossPortConformanceTests
.CsharpApiDocsSurface_MatchesTheSharedManifest — fails identically on the pristine
pre-change tree). Zero golden changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
…herited M:N/dataGrid/template/param-ref

Align the JVM (Java) validation passes to the TS reference (ADR-0039): where
the TS oracle RESOLVES an effective property to decide correctness, the Java
port must resolve too. Own-only reads on inheritable attrs/children wrongly
skip or reject metadata inherited via `extends`.

Sites flipped to resolving (each mirrors its TS counterpart):

- ValidationPhase.validateRelationshipsM2M: @through/@sourceRefField now read
  via the resolving getters (getThrough/getSourceRefField); the junction's
  identity.reference view (countJunctionReferences / junctionReferenceFkFields)
  now iterates getIdentities() (effective), mirroring TS referenceIdentities()
  and M2MFields.derive. Own-only would count 0 references on a junction that
  inherits its two identity.reference children via extends and falsely reject
  the M:N (ERR_INVALID_RELATIONSHIP) at load. (getObjectRef/getCardinality/
  isSymmetric already resolve.)
- ValidationPhase.validateDataGridLayouts: the layout.dataGrid lookup +
  @defaultSortField/@filter now resolve (an inherited grid is now validated),
  mirroring TS validateDataGridSortFields/FilterValues.
- ValidationPhase.validateTemplates: @format/@promptStyle/@kind + the email/
  document part-refs (@subjectRef/@htmlBodyRef/@textRef) now resolve — templates
  CAN extend (consistent with the merged codegen template decision), mirroring
  TS validateTemplatePayloadRefs. (@payloadRef/@requiredSlots getters + the
  payload field-name collection already resolved.)
- ValidationPhase.validateSourceParameterRef: @parameterRef, the parameter
  value-object's fields, and the referenced entity's field now resolve,
  mirroring TS validate-source-parameter-ref.ts (origin.* + @from stay own —
  origin never inherits, ADR-0029).
- RegisteredValidation: the registry-driven reference-attr read (@objectref on
  relationships, @references on identity.reference) now resolves, mirroring TS
  validation-registry.ts — own-only would let a dangling INHERITED reference
  slip through the resolution check.

Runtime/codegen M:N discovery was already resolving (getRelationships() no-arg,
M2MFields.derive, the OMDB/Spring/Kotlin resolvers) — no runtime-drop bug there.
Kotlin needs no source change: it shares the JVM metadata loader for validation
and its M:N discovery resolves. Genuinely-own declaration checks (required-attr
PRESENCE, @isAbstract, TPH markers, the junction's OWN identity.reference
declarations, explicit projection-identity @fields, origin.*) are left own.

Adds M2MSlimVocabularyTest regression coverage: an M:N whose junction inherits
both identity.reference children via extends loads cleanly AND derives its FK
direction (fails against the own-only iteration; confirmed).

Java metadata 1067 + codegen-spring 158 + codegen-kotlin 266 green; Java
integration 30 + Kotlin integration M2M 6 green (Testcontainers PG). No golden
changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GF9xLEQZaPus5Y6opk398n
@dmealing
dmealing merged commit 8afc616 into main Jul 1, 2026
18 checks passed
@dmealing
dmealing deleted the feat/own-phase2fix branch July 1, 2026 04:44
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.

2 participants