Skip to content

feat: cross-package reference resolution contract (ADR-0041) — all 5 ports + conformance matrix#173

Merged
dmealing merged 2 commits into
mainfrom
feat/xpkg-reference-resolution
Jul 5, 2026
Merged

feat: cross-package reference resolution contract (ADR-0041) — all 5 ports + conformance matrix#173
dmealing merged 2 commits into
mainfrom
feat/xpkg-reference-resolution

Conversation

@dmealing

@dmealing dmealing commented Jul 5, 2026

Copy link
Copy Markdown
Member

Why

Every reference that names another object (@objectRef, @references, origin @from/@of/@via, extends, @through, template refs) can cross package boundaries, but the ports had no shared, tested contract for name collisions across packages and zero cross-package conformance fixtures. That hid a real cross-port bug: with same-named entities in different packages, an explicit FQN @objectRef: "acme::vendor::Customer" resolved correctly in TS/C#/Python but bound the wrong package in Java (SymbolTable.nameMatches/ValidationPhase.nameMatches matched the ref's bare tail before the exact-FQN check → wrong FK table). Kotlin bare collisions were arbitrary first-match.

Contract (ADR-0041)

  • FQN reference → resolves exactly to its package; never a bare-tail fallback.
  • Bare reference → prefer the referrer's own package; else a unique object of that name anywhere; else ERR_AMBIGUOUS_REF (new). Author qualifies with the FQN to disambiguate.

Preserves every unique-name case (bare or FQN); only collisions change — from silent, arbitrary, port-divergent mis-binding to same-package preference or a loud, portable error.

What's here

  • ADR-0041 documenting the contract.
  • TS reference: resolveObjectRef + validateCrossPackageRefs loader pass + ERR_AMBIGUOUS_REF.
  • Fan-out to Java/Kotlin/Python/C# — Java carries the actual bug fix (FQN-exact matching); all ports add the ambiguity pass.
  • Cross-package conformance matrix (fixtures/conformance/xpkg-*, error-xpkg-*): every ref-bearing attr cross-package + the collision cases, incl. xpkg-collision-extends-fqn whose effective serialization catches the Java FQN-discard bug.

Verified — all 5 ports byte-identical green

Port Conformance
TypeScript 420 (+ metadata 2111, codegen-ts 909, cli/render, typecheck)
Java ConformanceTest 420/0 + others
Kotlin conformance green
Python 323/0
C# 710/0

Residual (follow-up, documented)

Codegen resolution (codegen-ts relation-resolver/import-path, and the JVM codegen helpers) still bare-name-first-match on a same-name collision — the loader now guards this (ERR_AMBIGUOUS_REF); codegen same-package target-selection is a separate pass, tracked for follow-up.

🤖 Generated with Claude Code

dmealing and others added 2 commits July 5, 2026 12:38
…ence) + conformance matrix (ADR-0041)

Every reference that names another object (@objectref, @references, origin
@from/@of/@via, extends, @through, template refs) may cross package boundaries,
but the ports had no shared, tested contract for name collisions across packages
and ZERO cross-package conformance fixtures — which hid a real cross-port bug
(Java resolves an explicit FQN to the wrong same-named package; investigation
this session).

Contract (ADR-0041): an FQN reference resolves EXACTLY to its package (never a
bare-tail fallback); a BARE reference prefers the referrer's own package, else a
unique object of that name anywhere, else ERR_AMBIGUOUS_REF (new). Preserves
every unique-name case; only collisions change — from silent, arbitrary,
port-divergent mis-binding to same-package preference or a loud, portable error.

TS reference implementation:
- `resolveObjectRef(root, ref, referrerPkg)` (naming-refs.ts) — the single
  object-ref resolver: FQN-exact; bare prefers referrer pkg, else unique, else
  ambiguous.
- `validateCrossPackageRefs` loader pass emits ERR_AMBIGUOUS_REF for a bare ref
  that names an object in >1 package with none in the referrer's package (covers
  every REF_BEARING_ATTR_NAMES attr; strips origin dotted `.child` tails to the
  owner). extends stays FQN-strict via its own FR-032 super-resolver (bare
  cross-package extends → ERR_UNRESOLVED_SUPER).
- ERR_AMBIGUOUS_REF registered in errors.ts + fixtures/conformance/ERROR-CODES.json.

Conformance matrix (fixtures/conformance/xpkg-*, error-xpkg-*): every ref-bearing
attr cross-package (unique-name working cases) + the collision contract
(FQN-exact-across-collision via extends effective serialization — the fixture that
catches the Java FQN-discard bug; bare same-package preference; bare-ambiguous →
error). TS green: metadata 2111, codegen-ts 909, cli/render, typecheck clean.

Residual (follow-up): codegen-ts resolution (relation-resolver/import-path) still
bare-name first-match on a same-name collision — loader now guards it, codegen
same-package preference is a separate pass. Port fan-out (Java/Python/C#/Kotlin)
next, all conformance-gated against this matrix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ew1XfYSbEAezxjs9opynAe
…ion contract (ADR-0041)

Fan-out of the TS reference (60bd97d). All 5 ports now pass the shared
cross-package conformance matrix byte-identically.

- Java (the confirmed bug): SymbolTable.nameMatches / ValidationPhase.nameMatches
  matched the ref's bare TAIL before the exact-FQN check, so an explicit FQN
  `xpkg::vendor::Customer` silently bound `xpkg::crm::Customer` (wrong package /
  wrong FK table). Now: an FQN ref (`ref.contains("::")`) resolves EXACTLY
  (`ref.equals(child.getName())`), never a bare-tail fallback; bare refs match the
  short name. Fixes xpkg-collision-extends-fqn (effective serialization now
  inherits from the correct package).
- All ports: ERR_AMBIGUOUS_REF registered + a validateCrossPackageRefs pass
  emitting it for a bare object-ref that names an object in >1 package with none
  in the referrer's own package (mirrors the TS validateCrossPackageRefs). Python
  and C# already resolved FQNs exactly (no matcher bug), so they only added the
  ambiguity pass + resolver helper.
- Codegen touched where it shared the bare-match bug (codegen-spring
  SpringM2mSupport, codegen-kotlin render helper); codegen same-package
  target-selection remains a follow-up (matching the TS residual note).

Verified: Java ConformanceTest 420/0 + Kotlin conformance green; Python 323/0;
C# 710/0. All emit ERR_AMBIGUOUS_REF on the ambiguity fixture and resolve the
FQN-collision fixture to the correct package.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ew1XfYSbEAezxjs9opynAe
@dmealing
dmealing merged commit d3d3495 into main Jul 5, 2026
1 check passed
@dmealing
dmealing deleted the feat/xpkg-reference-resolution branch July 5, 2026 17:03
dmealing added a commit that referenced this pull request Jul 5, 2026
…ollision fixture

Follow-up to PR #173. A code review found the Java M:N runtime/derivation
resolvers still carried the exact bare-tail FQN mis-bind bug ADR-0041 closes
(the same class as the confirmed bug, in the M:N path, Java-only — TS/Kotlin/C#/
Python M:N were already FQN-safe):
- metadata M2MFields (the cross-port FK-derivation SSOT): findObject stripped an
  FQN to its bare tail and matched getShortName(); isSelfJoin / findRefToEntity
  compared stripped bare tails — so a cross-package hetero M:N with a same-named
  entity mis-classified as a self-join / bound the wrong junction reference.
- omdb runtime M2MResolver.mustGetEntity: same stripPackage-then-short-name match
  on the FQN @objectRef/@through at runtime traversal.

Fix (mirrors the SpringM2mSupport fix from #173): resolve the @objectRef/@through
to an object FQN-exact (an FQN matches getName() exactly, never a bare-tail
fallback), and classify self-join / match junction references by RESOLVED object
identity, never a stripped tail. Bare-name/same-package behavior unchanged (the
broader bare-collision same-package preference stays #174).

Gated: new conformance fixture xpkg-m2n-collision (a cross-package M:N where the
target + junction share a bare name with entities in other packages, referenced
by FQN) — a bare-tail match would bind the wrong package. Expected generated from
the TS oracle. Green in all metamodel-conformance ports: TS 422, Java 422,
Python 324, C# 712 (Kotlin rides the shared Java loader). ADR-0041 Consequences
corrected to name the files actually changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ew1XfYSbEAezxjs9opynAe
dmealing added a commit that referenced this pull request Jul 5, 2026
…ollision fixture (#175)

Follow-up to PR #173. A code review found the Java M:N runtime/derivation
resolvers still carried the exact bare-tail FQN mis-bind bug ADR-0041 closes
(the same class as the confirmed bug, in the M:N path, Java-only — TS/Kotlin/C#/
Python M:N were already FQN-safe):
- metadata M2MFields (the cross-port FK-derivation SSOT): findObject stripped an
  FQN to its bare tail and matched getShortName(); isSelfJoin / findRefToEntity
  compared stripped bare tails — so a cross-package hetero M:N with a same-named
  entity mis-classified as a self-join / bound the wrong junction reference.
- omdb runtime M2MResolver.mustGetEntity: same stripPackage-then-short-name match
  on the FQN @objectRef/@through at runtime traversal.

Fix (mirrors the SpringM2mSupport fix from #173): resolve the @objectRef/@through
to an object FQN-exact (an FQN matches getName() exactly, never a bare-tail
fallback), and classify self-join / match junction references by RESOLVED object
identity, never a stripped tail. Bare-name/same-package behavior unchanged (the
broader bare-collision same-package preference stays #174).

Gated: new conformance fixture xpkg-m2n-collision (a cross-package M:N where the
target + junction share a bare name with entities in other packages, referenced
by FQN) — a bare-tail match would bind the wrong package. Expected generated from
the TS oracle. Green in all metamodel-conformance ports: TS 422, Java 422,
Python 324, C# 712 (Kotlin rides the shared Java loader). ADR-0041 Consequences
corrected to name the files actually changed.


Claude-Session: https://claude.ai/code/session_01Ew1XfYSbEAezxjs9opynAe

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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