@@ -231,7 +231,10 @@ def _resolve_collection_type(
231231 its ``@objectRef`` target, schedule a nested ``<TargetShortName>Payload``
232232 for in-file emission, return ``list[<TargetShortName>Payload]``.
233233
234- Dedupe across the whole run via *emitted_nested_fqns* (Kotlin parity)."""
234+ Dedupe is per-file via *emitted_nested_fqns* — if the same target is
235+ referenced by two fields in the same payload module, only one nested
236+ class is emitted. Cross-file dedupe would leave forward-references
237+ dangling (see the module docstring)."""
235238 via = origin .attr (ORIGIN_ATTR_VIA )
236239 if not isinstance (via , str ) or not via :
237240 return _fallback_type (fallback )
@@ -406,10 +409,11 @@ def render_payload_vo(
406409 lines .append ("from pydantic import BaseModel" )
407410 lines .append ("" )
408411 lines .append ("" )
409- # Emit nested classes FIRST so the primary class's ``list[NestedPayload]``
410- # references are forward-defined-free at import time (pydantic v2 supports
411- # postponed eval via ``from __future__ import annotations`` but emitting
412- # nested-first matches Kotlin's emission order and reads cleaner).
412+ # Emit nested classes FIRST. Pydantic v2 with `from __future__ import
413+ # annotations` evaluates field annotations lazily, but it needs every
414+ # referenced class to be defined in the module namespace at model-build
415+ # time — otherwise it raises PydanticUserError("not fully defined") and
416+ # callers would have to run model_rebuild(). Nested-first avoids that.
413417 for block in nested_blocks :
414418 lines .extend (block )
415419 lines .append ("" )
0 commit comments