Skip to content

fix(codegen): resolve @objectRef to bare class name + use resolution_key for header FQN#28

Merged
dmealing merged 1 commit into
mainfrom
fix/py-codegen-objectref-resolution-key
Jun 15, 2026
Merged

fix(codegen): resolve @objectRef to bare class name + use resolution_key for header FQN#28
dmealing merged 1 commit into
mainfrom
fix/py-codegen-objectref-resolution-key

Conversation

@dmealing

@dmealing dmealing commented Jun 14, 2026

Copy link
Copy Markdown
Member

What

Two Python-codegen correctness fixes surfaced when a payload's object.value
referenced another value-object via @objectRef across multiple metadata files.

1. @objectRef emitted as a raw FQN

@objectRef is FQN-expanded at load time (e.g. app::pkg::Thing). The value/
entity generator emitted the expanded form directly, producing invalid Python:

tools: list[app::pkg::Thing] | None = None
from .app::pkg::Thing import app::pkg::Thing

The type annotation and the relative import now use the bare class name
(type_map.py + entity_model.py), since all generated VOs live flat in one
package.

2. Header FQN folded onto the wrong package after a multi-file merge

The four codegen header-FQN helpers (entity_model, router,
filter_allowlist, payload_vo) did a naive nearest-ancestor package walk.
After a multi-file merge every node hangs under one package-less merged root,
so each object folded onto the alphabetically-first file's package — e.g. an
object declared in b.yaml (package b) would be labelled with package a.

They now delegate to the canonical MetaData.resolution_key() (own package →
file-default package captured at parse → nearest ancestor), which already
handles the merged-tree case. For single-file metadata this is identical to the
old walk, so existing output is unchanged; only the multi-file case is corrected.

Tests

Added regression tests in tests/codegen/test_entity_model.py for both: an
FQN-expanded @objectRef shortening to the class name, and the header FQN
folding the file-default package after a merge. Full codegen suite green.

🤖 Generated with Claude Code

…key for header FQN

Two multi-file / nested-ref codegen correctness fixes:
- @objectref is FQN-expanded at load time; the value/entity generator emitted
  list[pkg::Thing] and a 'from .pkg::Thing import' line (invalid Python). The
  type annotation and relative import now use the bare class name.
- The four header-FQN helpers did a naive nearest-ancestor package walk, so after
  a multi-file merge every object folded onto the alphabetically-first file's
  package. Route them all through MetaData.resolution_key() (own -> file-default
  -> ancestor), which already handles the merged-tree case.

Regression tests added for both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dmealing
dmealing merged commit 53e3507 into main Jun 15, 2026
29 checks passed
@dmealing
dmealing deleted the fix/py-codegen-objectref-resolution-key branch June 15, 2026 00:31
dmealing added a commit that referenced this pull request Jun 15, 2026
#30)

Cross-port audit of the two bug patterns fixed for Python in PR #28:

  BUG A — `@objectRef` emitted as a RAW FQN in a generated type reference /
          import, where it must be the BARE short name (last `::` segment).
  BUG B — a naive nearest-ancestor package walk for an entity's header FQN,
          where it must use the canonical resolution key.

Audit result across the four remaining ports:

  - Java (codegen-spring):  both clean — SpringNaming.splitFqn() already
    strips refs and derives package from the load-time-qualified entity name.
  - Kotlin (codegen-kotlin): both clean — same JVM splitFqn() strategy.
  - C#:  BUG A present in PayloadCodegen; BUG B clean
         (PackageBindingResolver.EffectivePackage already uses ResolutionKey).
  - TS:  BUG A present in three value-object/entity emitters; BUG B clean
         (docs-paths.effectivePackage already uses resolutionKey()).

BUG B is therefore not present in any port — every engine derives the entity
package from its canonical resolution key / load-time-qualified name. Only
BUG A needed fixing, in C# and TS:

  - C# PayloadCodegen.FieldType / BuildTree: a fully-qualified nested
    @objectref emitted `IReadOnlyList<acme::ai::Brief>` (invalid C#) AND the
    nested-record lookup (FindObject matches bare names) missed, dropping the
    nested record entirely. Now StripPkg() before use, matching every other
    C# generator.
  - TS valueObjectFieldType / zod insert-schema / sqlite isArray $type: a
    fully-qualified ref emitted an invalid identifier + a colon-laden sibling
    module path. Now stripPackage() before use, matching the existing string
    form fieldTsTypeString().

Regression tests added at each fixed site (C# PayloadCodegenTests, TS
column-mapper / zod-validators / value-object-file). The cross-package
behavior-corpus gate (a multi-file/multi-package @objectref roundtrip in
persistence-conformance) is deferred to the multi-package conformance work
per the standing FR-007 policy that codegen drift is gated through behavior
corpora, not a codegen-output corpus.

Co-authored-by: Claude Opus 4.8 (1M context) <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