metaobjects 0.19.1__tar.gz → 0.19.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) hide show
  1. {metaobjects-0.19.1 → metaobjects-0.19.3}/PKG-INFO +1 -1
  2. {metaobjects-0.19.1 → metaobjects-0.19.3}/pyproject.toml +1 -1
  3. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/entity_model.py +29 -12
  4. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/payload_vo_generator.py +196 -34
  5. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/errors.py +3 -0
  6. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/field.json +1 -1
  7. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/validator.json +2 -2
  8. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_staleness_nudge.py +4 -4
  9. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_payload_vo_generator.py +89 -0
  10. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_render_helper_conformance.py +43 -1
  11. {metaobjects-0.19.1 → metaobjects-0.19.3}/.gitignore +0 -0
  12. {metaobjects-0.19.1 → metaobjects-0.19.3}/LICENSE +0 -0
  13. {metaobjects-0.19.1 → metaobjects-0.19.3}/README.md +0 -0
  14. {metaobjects-0.19.1 → metaobjects-0.19.3}/hatch_build.py +0 -0
  15. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/__init__.py +0 -0
  16. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/agent_context/__init__.py +0 -0
  17. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/agent_context/scaffold.py +0 -0
  18. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/__init__.py +0 -0
  19. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/api_model.py +0 -0
  20. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/builder.py +0 -0
  21. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/naming.py +0 -0
  22. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/paths.py +0 -0
  23. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/apidocs/renderer.py +0 -0
  24. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/attr_class_map.py +0 -0
  25. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/cli.py +0 -0
  26. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  27. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/__init__.py +0 -0
  28. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/config.py +0 -0
  29. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/constants.py +0 -0
  30. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  31. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  32. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/format.py +0 -0
  33. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  34. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generator.py +0 -0
  35. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generator_registry.py +0 -0
  36. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/__init__.py +0 -0
  37. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  38. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  39. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  40. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  41. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  42. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  43. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  44. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/router_generator.py +0 -0
  45. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  46. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  47. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  48. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  49. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  50. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  51. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/runner.py +0 -0
  52. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  53. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  54. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  55. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  56. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  57. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  58. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/codegen/type_map.py +0 -0
  59. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/core_types.py +0 -0
  60. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/datatype.py +0 -0
  61. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/documentation/__init__.py +0 -0
  62. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/documentation/doc_constants.py +0 -0
  63. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/documentation/doc_provider.py +0 -0
  64. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/documentation/doc_schema.py +0 -0
  65. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/__init__.py +0 -0
  66. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/merge.py +0 -0
  67. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/meta_data_loader.py +0 -0
  68. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/registered_validation.py +0 -0
  69. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/__init__.py +0 -0
  70. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/directory_source.py +0 -0
  71. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/file_source.py +0 -0
  72. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  73. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/sources/uri_source.py +0 -0
  74. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_discriminator.py +0 -0
  75. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_field_readonly.py +0 -0
  76. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  77. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  78. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  79. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/loader/validation_passes.py +0 -0
  80. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/__init__.py +0 -0
  81. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/__init__.py +0 -0
  82. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  83. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  84. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  85. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/field/__init__.py +0 -0
  86. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  87. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  88. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  89. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  90. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  91. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/index/__init__.py +0 -0
  92. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  93. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  94. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/__init__.py +0 -0
  95. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  96. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  97. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  98. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  99. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  100. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/object/value_object.py +0 -0
  101. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  102. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  103. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  104. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  105. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  106. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  107. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/meta_data.py +0 -0
  108. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/meta_root.py +0 -0
  109. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/__init__.py +0 -0
  110. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  111. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  112. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  113. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  114. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  115. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  116. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  117. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  118. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  119. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/__init__.py +0 -0
  120. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  121. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  122. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  123. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  124. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  125. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  126. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  127. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  128. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/provider_extends.py +0 -0
  129. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/template/__init__.py +0 -0
  130. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/template/meta_template.py +0 -0
  131. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  132. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/meta/template/template_constants.py +0 -0
  133. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/naming_refs.py +0 -0
  134. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/parser.py +0 -0
  135. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/parser_yaml.py +0 -0
  136. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/provider.py +0 -0
  137. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/py.typed +0 -0
  138. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/registry.py +0 -0
  139. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/registry_manifest.py +0 -0
  140. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/__init__.py +0 -0
  141. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/email_document.py +0 -0
  142. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/escapers.py +0 -0
  143. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  144. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/__init__.py +0 -0
  145. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/coerce.py +0 -0
  146. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/extract.py +0 -0
  147. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/extract_map.py +0 -0
  148. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  149. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/locate.py +0 -0
  150. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/normalize.py +0 -0
  151. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/strip.py +0 -0
  152. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/types.py +0 -0
  153. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  154. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/filesystem_provider.py +0 -0
  155. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/__init__.py +0 -0
  156. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  157. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  158. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  159. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  160. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  161. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/renderer.py +0 -0
  162. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/render/verify.py +0 -0
  163. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/__init__.py +0 -0
  164. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/llm_recorder.py +0 -0
  165. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  166. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/object_manager.py +0 -0
  167. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/runtime/tph.py +0 -0
  168. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/serializer_json.py +0 -0
  169. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/shared/__init__.py +0 -0
  170. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/shared/base_types.py +0 -0
  171. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/shared/separators.py +0 -0
  172. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/shared/structural.py +0 -0
  173. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/__init__.py +0 -0
  174. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/error_source.py +0 -0
  175. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/json_path.py +0 -0
  176. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/semantic_diff.py +0 -0
  177. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/source/yaml_positions.py +0 -0
  178. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  179. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/attr.json +0 -0
  180. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/db.json +0 -0
  181. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  182. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/identity.json +0 -0
  183. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/index.json +0 -0
  184. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/layout.json +0 -0
  185. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/object.json +0 -0
  186. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/origin.json +0 -0
  187. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  188. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  189. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/source.json +0 -0
  190. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/template.json +0 -0
  191. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  192. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/ui.json +0 -0
  193. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/spec_metamodel/view.json +0 -0
  194. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/super_resolve.py +0 -0
  195. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/validation_types.py +0 -0
  196. {metaobjects-0.19.1 → metaobjects-0.19.3}/src/metaobjects/yaml_desugar.py +0 -0
  197. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/__init__.py +0 -0
  198. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/__init__.py +0 -0
  199. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  200. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/extends/expected/Program.py +0 -0
  201. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/extends/meta.json +0 -0
  202. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  203. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  204. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/nested-array/meta.json +0 -0
  205. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  206. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  207. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/scalars/meta.json +0 -0
  208. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  209. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/golden/vanilla/meta.json +0 -0
  210. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_abstract_conformance.py +0 -0
  211. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_api_docs_builder.py +0 -0
  212. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_api_docs_paths.py +0 -0
  213. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli.py +0 -0
  214. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_providers.py +0 -0
  215. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_registry.py +0 -0
  216. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_verify_strict.py +0 -0
  217. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  218. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_constants_config.py +0 -0
  219. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_entity_model.py +0 -0
  220. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_enum_conformance.py +0 -0
  221. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  222. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_extractor_generator.py +0 -0
  223. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  224. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_format.py +0 -0
  225. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  226. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_fr010_output_codegen.py +0 -0
  227. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  228. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_generator.py +0 -0
  229. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_generator_extension_seams.py +0 -0
  230. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_golden.py +0 -0
  231. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  232. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_inheritance_conformance.py +0 -0
  233. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_instance_artifacts.py +0 -0
  234. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_m2m_codegen.py +0 -0
  235. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_origin_nullability.py +0 -0
  236. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_output_parser_generator.py +0 -0
  237. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_overwrite_policy.py +0 -0
  238. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_projection_compile.py +0 -0
  239. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_render_helper_generator.py +0 -0
  240. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_reverse_finders.py +0 -0
  241. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_router_generator.py +0 -0
  242. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_runner.py +0 -0
  243. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_data.py +0 -0
  244. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_generator.py +0 -0
  245. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_output_pattern.py +0 -0
  246. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_scope_helpers.py +0 -0
  247. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_scope_walk.py +0 -0
  248. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_template_spec.py +0 -0
  249. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_tph_codegen.py +0 -0
  250. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_trace_helper_generator.py +0 -0
  251. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_type_map.py +0 -0
  252. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_validation_conformance.py +0 -0
  253. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/codegen/test_write_through.py +0 -0
  254. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/__init__.py +0 -0
  255. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/capabilities.py +0 -0
  256. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/conformance-expected-failures.json +0 -0
  257. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/conformance_adapter.py +0 -0
  258. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/corpus.py +0 -0
  259. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/expected_failures.py +0 -0
  260. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/fixture_discovery.py +0 -0
  261. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/navigator.py +0 -0
  262. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  263. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_conformance.py +0 -0
  264. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_extract_conformance.py +0 -0
  265. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_extract_object_verdict.py +0 -0
  266. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  267. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_fr011_attrs.py +0 -0
  268. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_generator_registry_conformance.py +0 -0
  269. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_object_model_conformance.py +0 -0
  270. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_provider_composition_conformance.py +0 -0
  271. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_registry_conformance.py +0 -0
  272. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_runner_hardfail.py +0 -0
  273. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  274. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_strict_attr_load.py +0 -0
  275. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_template_codegen_conformance.py +0 -0
  276. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_template_generator_conformance.py +0 -0
  277. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/test_yaml_conformance.py +0 -0
  278. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  279. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/__init__.py +0 -0
  280. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/api_contract_assertions.py +0 -0
  281. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/api_contract_jsonb_server.py +0 -0
  282. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/api_contract_m2m_server.py +0 -0
  283. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/api_contract_server.py +0 -0
  284. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/generated_jsonb_app.py +0 -0
  285. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/generated_m2m_app.py +0 -0
  286. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/generated_router_app.py +0 -0
  287. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/generated_tph_app.py +0 -0
  288. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/meta_ai_trace.yaml +0 -0
  289. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/normalization.py +0 -0
  290. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/postgres_container.py +0 -0
  291. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/query_runner.py +0 -0
  292. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/scenarios.py +0 -0
  293. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract.py +0 -0
  294. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_generated.py +0 -0
  295. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_jsonb.py +0 -0
  296. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_m2m.py +0 -0
  297. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  298. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_api_contract_tph_generated.py +0 -0
  299. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  300. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_llm_call_trace.py +0 -0
  301. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_normalization.py +0 -0
  302. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_query_scenarios.py +0 -0
  303. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/integration/test_runtime_return_types.py +0 -0
  304. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/open_closed_proof_test.py +0 -0
  305. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/__init__.py +0 -0
  306. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/__init__.py +0 -0
  307. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_coerce.py +0 -0
  308. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_extract.py +0 -0
  309. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_extract_map.py +0 -0
  310. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  311. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_locate.py +0 -0
  312. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_model.py +0 -0
  313. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_normalize.py +0 -0
  314. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_strip.py +0 -0
  315. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  316. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/prompt/__init__.py +0 -0
  317. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/prompt/test_output_format_renderer.py +0 -0
  318. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_email_document.py +0 -0
  319. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_filesystem_provider.py +0 -0
  320. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_output_format_renderer_nested.py +0 -0
  321. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_output_prompt_conformance.py +0 -0
  322. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_render_conformance.py +0 -0
  323. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_render_max_chars.py +0 -0
  324. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_verify.py +0 -0
  325. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/render/test_verify_conformance.py +0 -0
  326. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/__init__.py +0 -0
  327. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_fr5c_merge_attribution.py +0 -0
  328. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_fr5d_reference_resolution.py +0 -0
  329. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_fr5e_database_source_shape.py +0 -0
  330. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_json_path.py +0 -0
  331. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_semantic_diff.py +0 -0
  332. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_source_on_node.py +0 -0
  333. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/source/test_yaml_positions.py +0 -0
  334. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/test_api_docs_accuracy.py +0 -0
  335. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/__init__.py +0 -0
  336. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_agent_context_staleness.py +0 -0
  337. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_capabilities.py +0 -0
  338. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_common_attrs.py +0 -0
  339. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_core_types.py +0 -0
  340. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_effective_package.py +0 -0
  341. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_errors.py +0 -0
  342. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_field_enum.py +0 -0
  343. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_field_map_validation.py +0 -0
  344. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  345. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  346. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_index_lookup.py +0 -0
  347. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_llm_recorder.py +0 -0
  348. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_loader.py +0 -0
  349. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_loader_bom.py +0 -0
  350. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_loader_class.py +0 -0
  351. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_merge.py +0 -0
  352. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_meta_attr.py +0 -0
  353. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_meta_data.py +0 -0
  354. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_meta_source.py +0 -0
  355. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_module_shortcuts.py +0 -0
  356. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_n2m_resolver.py +0 -0
  357. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  358. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  359. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  360. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_object_manager_write_through.py +0 -0
  361. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_one_primary_source.py +0 -0
  362. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_parser.py +0 -0
  363. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_parser_yaml.py +0 -0
  364. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_provider.py +0 -0
  365. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_provider_extension.py +0 -0
  366. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_registry.py +0 -0
  367. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_registry_completeness.py +0 -0
  368. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_registry_extend.py +0 -0
  369. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_registry_sealed.py +0 -0
  370. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_relationship_referential_actions.py +0 -0
  371. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_resolution_key.py +0 -0
  372. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  373. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_serializer.py +0 -0
  374. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_shared_constants.py +0 -0
  375. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_smoke.py +0 -0
  376. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_source_escapes.py +0 -0
  377. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_sources.py +0 -0
  378. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_strict_child_placement.py +0 -0
  379. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_super_resolve.py +0 -0
  380. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_template_toolcall.py +0 -0
  381. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  382. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_attr_schema.py +0 -0
  383. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_filter_values.py +0 -0
  384. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_origin_195.py +0 -0
  385. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_origin_paths.py +0 -0
  386. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_sort_field.py +0 -0
  387. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_validation_warnings.py +0 -0
  388. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_write_coercion_arrays.py +0 -0
  389. {metaobjects-0.19.1 → metaobjects-0.19.3}/tests/unit/test_yaml_desugar.py +0 -0
  390. {metaobjects-0.19.1 → metaobjects-0.19.3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaobjects
3
- Version: 0.19.1
3
+ Version: 0.19.3
4
4
  Summary: Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port.
5
5
  Project-URL: Homepage, https://metaobjects.dev
6
6
  Project-URL: Repository, https://github.com/metaobjectsdev/metaobjects
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "metaobjects"
3
- version = "0.19.1"
3
+ version = "0.19.3"
4
4
  description = "Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -99,7 +99,7 @@ def _first_attr(field: MetaField, sub_type: str, attr_name: str) -> object | Non
99
99
  return None
100
100
 
101
101
 
102
- def _validator_constraints(field: MetaField, wire: bool = False) -> dict[str, object]:
102
+ def _validator_constraints(field: MetaField, *, wire: bool) -> dict[str, object]:
103
103
  """Map the field's ``validator.*`` children + field attrs to Pydantic ``Field``
104
104
  kwargs. Cross-port-canonical semantics (TS is the reference):
105
105
 
@@ -114,8 +114,13 @@ def _validator_constraints(field: MetaField, wire: bool = False) -> dict[str, ob
114
114
  every OTHER constraint above unconditionally but must not self-validate Pin 1
115
115
  at construction (that broke a downstream adopter's in-process prompt-payload
116
116
  ``object.value`` models on legitimately-empty slots). Callers building a read
117
- model (``_field_line``) pass the default ``False``; callers building a wire
118
- model (``_create_field_line``/``_patch_field_line``) pass ``True``.
117
+ model (``_field_line``) pass ``wire=False``; callers building a wire model
118
+ (``_create_field_line``/``_patch_field_line``) pass ``wire=True``.
119
+
120
+ *wire* is keyword-only and REQUIRED (no default, item 5 of the #224
121
+ cleanup): a forgotten flag at a future wire call site must fail loudly
122
+ (``TypeError``) at generation time rather than silently falling back to a
123
+ permissive read-model tier.
119
124
  """
120
125
  kwargs: dict[str, object] = {}
121
126
 
@@ -171,17 +176,26 @@ def _validator_constraints(field: MetaField, wire: bool = False) -> dict[str, ob
171
176
  kwargs["pattern"] = _HOSTNAME_REGEX
172
177
 
173
178
  # FR-036 Pin 1 (#224 — WIRE TIER ONLY) — a @required non-array string is
174
- # non-empty: reject null / "" but ACCEPT whitespace-only. Emit an implicit
175
- # min_length of 1, keeping the stricter floor if a validator.length @min
176
- # already set one. Never applied to the in-process read model (wire=False).
179
+ # non-empty by default: reject null / "" but ACCEPT whitespace-only. Emit an
180
+ # implicit min_length of 1 ONLY when no validator.length @min was authored.
181
+ # Never applied to the in-process read model (wire=False).
182
+ #
183
+ # An explicitly authored @min is always AUTHORITATIVE over the implicit floor
184
+ # (ADR-0044 ruling on #224) — @min: 0 restores "must be provided, may be
185
+ # empty" by opting out of the non-empty floor entirely. Per the cross-port
186
+ # emission pin, an authored @min: 0 emits NO minimum constraint at all
187
+ # (cleanest, deterministic output across ports), so it is popped rather than
188
+ # kept as min_length=0.
177
189
  if (
178
190
  wire
179
191
  and field.sub_type == fc.FIELD_SUBTYPE_STRING
180
192
  and not field_is_array(field)
181
193
  and field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
182
194
  ):
183
- existing_min = kwargs.get("min_length")
184
- kwargs["min_length"] = max(1, existing_min) if isinstance(existing_min, int) else 1
195
+ if min_len is None:
196
+ kwargs["min_length"] = 1
197
+ elif min_len == 0:
198
+ kwargs.pop("min_length", None)
185
199
 
186
200
  return kwargs
187
201
 
@@ -196,7 +210,7 @@ def _constraint_parts(constraints: dict[str, object]) -> list[str]:
196
210
 
197
211
 
198
212
  def _type_expr_for_field(
199
- field: MetaField, imports: set[str], config: GenConfig, wire: bool = False
213
+ field: MetaField, imports: set[str], config: GenConfig, *, wire: bool
200
214
  ) -> tuple[str, str | None]:
201
215
  """The Python annotation text for *field* (arrays wrapped in ``list[...]``) plus the
202
216
  shared-enum class name when the field resolves to one (used to render an enum
@@ -211,7 +225,10 @@ def _type_expr_for_field(
211
225
  ``@objectRef`` annotates with the referenced VO's own WIRE model (``<Ref>Create``)
212
226
  rather than its read model, so a required-string Pin 1 violation nested inside the
213
227
  VO still validates on the wire (api-contract jsonb r10). A VO's own Create lines
214
- are emitted through this same wire path, so nested-VO-in-VO recurses naturally."""
228
+ are emitted through this same wire path, so nested-VO-in-VO recurses naturally.
229
+
230
+ *wire* is keyword-only and REQUIRED (no default, item 5 of the #224 cleanup)
231
+ — see ``_validator_constraints`` for the rationale."""
215
232
  shared = (
216
233
  fr019.shared_enum_for_field(field)
217
234
  if field.sub_type == fc.FIELD_SUBTYPE_ENUM
@@ -275,7 +292,7 @@ def origin_guaranteed_non_null(field: MetaField) -> bool:
275
292
 
276
293
  def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple[str, bool]:
277
294
  """Return (source line, uses_field). Collects required imports into *imports*."""
278
- type_expr, enum_type_name = _type_expr_for_field(field, imports, config)
295
+ type_expr, enum_type_name = _type_expr_for_field(field, imports, config, wire=False)
279
296
  required = field.attrs().get(fc.FIELD_ATTR_REQUIRED) is True
280
297
  default_raw = field.attrs().get(fc.FIELD_ATTR_DEFAULT)
281
298
  # A server-side expression default (now(), gen_random_uuid(), CURRENT_TIMESTAMP)
@@ -283,7 +300,7 @@ def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple
283
300
  # its required/optional shape). Only literal defaults become Pydantic defaults.
284
301
  has_default = default_raw is not None and not _is_sql_expr_default(default_raw)
285
302
 
286
- parts = _constraint_parts(_validator_constraints(field))
303
+ parts = _constraint_parts(_validator_constraints(field, wire=False))
287
304
  uses_field = bool(parts)
288
305
 
289
306
  # @default — render an enum member (Type.MEMBER, UPPER_CASE) or a Python literal.
@@ -69,6 +69,13 @@ from metaobjects.shared.separators import PACKAGE_SEP
69
69
 
70
70
  _GENERATOR_NAME = "payload-vo-generator"
71
71
 
72
+ # ADR-0044 backstop error code — a codegen-time (not loader) error, peer of the
73
+ # render tier's ERR_VAR_NOT_ON_PAYLOAD. Declared LOCALLY here for the same reason
74
+ # the TS reference (codegen-ts/src/payload-codegen.ts) declares it locally rather
75
+ # than in the shared cross-port ledger: promoting it to the ledger is coordinated
76
+ # with the Java/Kotlin follow-up so no port reddens on a code it doesn't yet emit.
77
+ ERR_PAYLOAD_NAME_COLLISION = "ERR_PAYLOAD_NAME_COLLISION"
78
+
72
79
 
73
80
  # ---------------------------------------------------------------------------
74
81
  # Naming helpers (snake_case mirrors the router/output-parser local helpers).
@@ -193,13 +200,17 @@ def _resolve_object_field_type(
193
200
  field: MetaField,
194
201
  root: MetaData,
195
202
  nested_emit_queue: list[tuple[MetaObject, str]],
196
- emitted_nested_fqns: set[str],
203
+ emitted_nested_keys: set[str],
204
+ name_map: dict[str, str],
197
205
  ) -> tuple[str, set[str]]:
198
206
  """A plain ``field.object`` (``@objectRef``, no origin child) — resolve to the
199
- nested ``<TargetShortName>Payload`` (single) or ``list[<TargetShortName>Payload]``
200
- (array). The target VO is scheduled for in-file emission (per-file dedupe, same
201
- mechanism as ``origin.collection``). Falls back to the bare type-map form when the
202
- ``@objectRef`` can't be resolved (defensive loader validation gates it first)."""
207
+ nested payload class (single) or ``list[...]`` (array). The class name comes
208
+ from the ADR-0044 *name_map* (bare ``<Short>Payload`` when unique in the module
209
+ closure, package-qualified on a cross-package short-name collision). The target
210
+ VO is scheduled for in-file emission, deduped by ``resolution_key()`` (NOT
211
+ ``fqn()``, which returns the bare name and would collapse two same-short-name
212
+ VOs from different packages into one). Falls back to the bare type-map form when
213
+ the ``@objectRef`` can't be resolved (defensive — loader validation gates it)."""
203
214
  ref = field.attrs().get(fc.FIELD_ATTR_OBJECT_REF)
204
215
  if not isinstance(ref, str) or not ref:
205
216
  return _fallback_type(field)
@@ -211,12 +222,14 @@ def _resolve_object_field_type(
211
222
  # both loaded trees (file_default_package) and programmatic trees (root package).
212
223
  referrer_pkg = _pkg_of(field.parent) if field.parent is not None else ""
213
224
  target = resolve_object_ref(root, ref, referrer_pkg)
214
- if target is None:
225
+ if not isinstance(target, MetaObject): # narrows MetaData|None -> MetaObject
215
226
  return _fallback_type(field)
216
- nested_class = payload_class_name(target.name)
217
- target_fqn = target.fqn()
218
- if target_fqn not in emitted_nested_fqns:
219
- emitted_nested_fqns.add(target_fqn)
227
+ target_key = target.resolution_key()
228
+ # name_map was populated by the ADR-0044 closure walk (pass 1/2), which visits
229
+ # the identical targets; the fallback guards a defensive gap only.
230
+ nested_class = name_map.get(target_key) or payload_class_name(target.name)
231
+ if target_key not in emitted_nested_keys:
232
+ emitted_nested_keys.add(target_key)
220
233
  nested_emit_queue.append((target, nested_class))
221
234
  if type_map.field_is_array(field):
222
235
  return f"list[{nested_class}]", set()
@@ -345,16 +358,20 @@ def _resolve_collection_type(
345
358
  root: MetaData,
346
359
  fallback: MetaField,
347
360
  nested_emit_queue: list[tuple[MetaObject, str]],
348
- emitted_nested_fqns: set[str],
361
+ emitted_nested_keys: set[str],
362
+ name_map: dict[str, str],
349
363
  ) -> tuple[str, set[str]]:
350
364
  """``origin.collection @via "Parent.rel"`` — walk Parent's relationship to
351
- its ``@objectRef`` target, schedule a nested ``<TargetShortName>Payload``
352
- for in-file emission, return ``list[<TargetShortName>Payload]``.
353
-
354
- Dedupe is per-file via *emitted_nested_fqns* — if the same target is
355
- referenced by two fields in the same payload module, only one nested
356
- class is emitted. Cross-file dedupe would leave forward-references
357
- dangling (see the module docstring)."""
365
+ its ``@objectRef`` target, schedule a nested payload class for in-file
366
+ emission, return ``list[<NestedClass>]``. The class name comes from the
367
+ ADR-0044 *name_map* (bare when unique in the module closure, package-qualified
368
+ on a cross-package short-name collision).
369
+
370
+ Dedupe is per-file via *emitted_nested_keys*, keyed by ``resolution_key()`` —
371
+ if the same target is referenced by two fields in the same payload module,
372
+ only one nested class is emitted; two same-short-name targets in DIFFERENT
373
+ packages are NOT collapsed (each keeps its own key + qualified name).
374
+ Cross-file dedupe would leave forward-references dangling (module docstring)."""
358
375
  via = origin.attr(ORIGIN_ATTR_VIA) # ADR-0039 sanctioned own: origin.* never inherits (ADR-0029)
359
376
  if not isinstance(via, str) or not via:
360
377
  return _fallback_type(fallback)
@@ -374,11 +391,10 @@ def _resolve_collection_type(
374
391
  target = _resolve_object_by_short_or_fqn(root, target_ref)
375
392
  if target is None:
376
393
  return _fallback_type(fallback)
377
- # MetaData.name is the short name (no `::`); see _resolve_object_by_short_or_fqn.
378
- nested_class = payload_class_name(target.name)
379
- target_fqn = target.fqn()
380
- if target_fqn not in emitted_nested_fqns:
381
- emitted_nested_fqns.add(target_fqn)
394
+ target_key = target.resolution_key()
395
+ nested_class = name_map.get(target_key) or payload_class_name(target.name)
396
+ if target_key not in emitted_nested_keys:
397
+ emitted_nested_keys.add(target_key)
382
398
  nested_emit_queue.append((target, nested_class))
383
399
  return f"list[{nested_class}]", set()
384
400
 
@@ -393,8 +409,9 @@ def _resolve_field_type(
393
409
  field: MetaField,
394
410
  root: MetaData,
395
411
  nested_emit_queue: list[tuple[MetaObject, str]],
396
- emitted_nested_fqns: set[str],
412
+ emitted_nested_keys: set[str],
397
413
  enum_aliases: dict[str, str],
414
+ name_map: dict[str, str],
398
415
  ) -> tuple[str, set[str]]:
399
416
  """Resolve the Python annotation for one payload-VO field, honoring any
400
417
  ``origin.*`` child. Falls back to ``type_map.py_type_for`` when none.
@@ -415,7 +432,7 @@ def _resolve_field_type(
415
432
  # reference an undefined bare entity name (Pydantic "not fully defined").
416
433
  if field.sub_type == fc.FIELD_SUBTYPE_OBJECT:
417
434
  return _resolve_object_field_type(
418
- field, root, nested_emit_queue, emitted_nested_fqns
435
+ field, root, nested_emit_queue, emitted_nested_keys, name_map
419
436
  )
420
437
  # A ``field.enum`` → Literal[...] (inline) or a named module alias (shared).
421
438
  enum_type = _enum_field_type(field, enum_aliases)
@@ -429,11 +446,142 @@ def _resolve_field_type(
429
446
  return _resolve_aggregate_type(origin, root, field)
430
447
  if origin.sub_type == "collection":
431
448
  return _resolve_collection_type(
432
- origin, root, field, nested_emit_queue, emitted_nested_fqns
449
+ origin, root, field, nested_emit_queue, emitted_nested_keys, name_map
433
450
  )
434
451
  return _fallback_type(field)
435
452
 
436
453
 
454
+ # ---------------------------------------------------------------------------
455
+ # ADR-0044 — collision-scoped nested-payload naming (three-pass pipeline).
456
+ #
457
+ # A payload module is a SINGLE self-contained file, so its `@objectRef` /
458
+ # `origin.collection` closure is the collision domain (ADR-0044's "closure of the
459
+ # payload root(s) emitted into one artifact"). Two value-objects sharing a bare
460
+ # short name across packages (`acme::alpha::Note` + `acme::beta::Note`, both
461
+ # reachable from one payload) must emit as DISTINCT classes — the pre-ADR-0044
462
+ # code deduped by `fqn()` (which returns the BARE name here), collapsing them to
463
+ # one class, last-wins, silently dropping the second shape. Pass 1 collects the
464
+ # closure keyed by `resolution_key()` (the true package-qualified FQN); pass 2
465
+ # assigns names as a pure function of the closure (bare when unique, package-
466
+ # qualified on collision, hard fail on a still-colliding derived name); pass 3
467
+ # (the existing emit path) uses the name map for both declaration and reference.
468
+ # ---------------------------------------------------------------------------
469
+
470
+
471
+ def _package_qualified_name(pkg: str, short_name: str) -> str:
472
+ """ADR-0044 — PascalCase each ``::``-segment of *pkg*, concatenate, append the
473
+ bare *short_name* (``acme::alpha`` + ``Note`` → ``AcmeAlphaNote``). A root-level
474
+ (empty-package) node keeps its bare short name — the loader's own-package
475
+ uniqueness already precludes two root-level VOs sharing a name, so this can't
476
+ silently under-qualify."""
477
+ if pkg == "":
478
+ return short_name
479
+ return "".join(_pascal(seg) for seg in pkg.split(PACKAGE_SEP)) + short_name
480
+
481
+
482
+ def _nested_target_of(field: MetaField, root: MetaData) -> MetaObject | None:
483
+ """The nested-payload target VO a *field* contributes to the module closure,
484
+ or ``None`` when it contributes no nested class. Mirrors the resolution in
485
+ :func:`_resolve_object_field_type` (a plain ``field.object`` ``@objectRef``)
486
+ and :func:`_resolve_collection_type` (``origin.collection`` ``@via``) EXACTLY,
487
+ so the ADR-0044 closure walk and the emission walk agree on the target set.
488
+ ``origin.passthrough`` / ``origin.aggregate`` yield scalar types (no nested
489
+ class) and so contribute nothing."""
490
+ origin = _find_origin_child(field)
491
+ if origin is None:
492
+ if field.sub_type != fc.FIELD_SUBTYPE_OBJECT:
493
+ return None
494
+ ref = field.attrs().get(fc.FIELD_ATTR_OBJECT_REF)
495
+ if not isinstance(ref, str) or not ref:
496
+ return None
497
+ referrer_pkg = _pkg_of(field.parent) if field.parent is not None else ""
498
+ target = resolve_object_ref(root, ref, referrer_pkg)
499
+ return target if isinstance(target, MetaObject) else None
500
+ if origin.sub_type == "collection":
501
+ via = origin.attr(ORIGIN_ATTR_VIA) # ADR-0039 sanctioned own: origin.* never inherits
502
+ if not isinstance(via, str) or not via:
503
+ return None
504
+ parts = _split_dotted_ref(via)
505
+ if parts is None:
506
+ return None
507
+ parent_name, rel_name = parts
508
+ parent = _resolve_object_by_short_or_fqn(root, parent_name)
509
+ if parent is None:
510
+ return None
511
+ rel = _find_relationship_on(parent, rel_name)
512
+ if rel is None:
513
+ return None
514
+ target_ref = rel.object_ref()
515
+ if not target_ref:
516
+ return None
517
+ return _resolve_object_by_short_or_fqn(root, target_ref)
518
+ return None
519
+
520
+
521
+ def _collect_nested_closure(
522
+ root: MetaData,
523
+ payload_vo: MetaObject,
524
+ closure: dict[str, MetaObject],
525
+ seen: set[str],
526
+ ) -> None:
527
+ """ADR-0044 pass 1 — walk *payload_vo*'s transitive nested-payload closure,
528
+ collecting each target VO keyed by ``resolution_key()`` (never ``fqn()`` —
529
+ which returns the bare name and would collapse a cross-package short-name
530
+ collision). *seen* is seeded with the primary payload VO's key so the primary
531
+ (which is named after the TEMPLATE, outside the VO-short-name collision
532
+ domain) is never treated as a nested class, and doubles as the cycle guard."""
533
+ for field in payload_vo.fields():
534
+ if not isinstance(field, MetaField):
535
+ continue
536
+ target = _nested_target_of(field, root)
537
+ if target is None:
538
+ continue
539
+ key = target.resolution_key()
540
+ if key in seen:
541
+ continue
542
+ seen.add(key)
543
+ closure[key] = target
544
+ _collect_nested_closure(root, target, closure, seen)
545
+
546
+
547
+ def _assign_nested_names(closure: dict[str, MetaObject]) -> dict[str, str]:
548
+ """ADR-0044 pass 2 — ``resolution_key()`` → emitted class name. A PURE function
549
+ of the closure's ``(key, short-name, package)`` triples, never of traversal
550
+ order: a bare short name unique in the closure emits ``<Short>Payload``
551
+ (byte-identical to pre-ADR-0044 output); a short-name collision emits EVERY
552
+ member under its package-qualified derived name
553
+ (``<PkgSegmentsPascal><Short>Payload``). If two distinct keys still derive the
554
+ same name, fail loud with ``ERR_PAYLOAD_NAME_COLLISION`` — never silently
555
+ collide a second time."""
556
+ by_short: dict[str, list[str]] = {}
557
+ for key, node in closure.items():
558
+ by_short.setdefault(node.name, []).append(key)
559
+
560
+ name_map: dict[str, str] = {}
561
+ for short, keys in by_short.items():
562
+ if len(keys) == 1:
563
+ name_map[keys[0]] = payload_class_name(short)
564
+ continue
565
+ for key in keys:
566
+ node = closure[key]
567
+ name_map[key] = payload_class_name(_package_qualified_name(_pkg_of(node), short))
568
+
569
+ # Backstop — sorted by key so both the emptiness of the colliding set and the
570
+ # pair named in the message are a pure function of the closure, not dict order.
571
+ owner: dict[str, str] = {}
572
+ for key in sorted(name_map):
573
+ emitted = name_map[key]
574
+ existing = owner.get(emitted)
575
+ if existing is not None and existing != key:
576
+ raise ValueError(
577
+ f"{ERR_PAYLOAD_NAME_COLLISION}: payload record name collision: "
578
+ f'"{emitted}" derives from both "{existing}" and "{key}" — rename one '
579
+ "value-object or move it to a package that derives a distinct name"
580
+ )
581
+ owner[emitted] = key
582
+ return name_map
583
+
584
+
437
585
  # ---------------------------------------------------------------------------
438
586
  # Class-block emission.
439
587
  # ---------------------------------------------------------------------------
@@ -444,10 +592,11 @@ def _emit_payload_class(
444
592
  payload_vo: MetaObject,
445
593
  root: MetaData,
446
594
  nested_emit_queue: list[tuple[MetaObject, str]],
447
- emitted_nested_fqns: set[str],
595
+ emitted_nested_keys: set[str],
448
596
  extra_imports: set[str],
449
597
  enum_aliases: dict[str, str],
450
598
  docstring: str,
599
+ name_map: dict[str, str],
451
600
  ) -> list[str]:
452
601
  """Build the source lines for one Pydantic ``BaseModel`` subclass."""
453
602
  lines: list[str] = [f"class {class_name}(BaseModel):", f' """{docstring}"""']
@@ -456,7 +605,7 @@ def _emit_payload_class(
456
605
  if not isinstance(field, MetaField):
457
606
  continue
458
607
  annotation, imports = _resolve_field_type(
459
- field, root, nested_emit_queue, emitted_nested_fqns, enum_aliases
608
+ field, root, nested_emit_queue, emitted_nested_keys, enum_aliases, name_map
460
609
  )
461
610
  extra_imports.update(imports)
462
611
  # Optionality mirrors the cross-port (TS) payload-codegen: a ``@required``
@@ -515,9 +664,18 @@ def render_payload_vo(
515
664
  if payload is None:
516
665
  return None
517
666
 
667
+ # ADR-0044 pass 1/2 — the collision domain is this module's nested-payload
668
+ # closure. Seed `seen` with the primary VO's key so the primary (named after
669
+ # the TEMPLATE, not the VO) stays out of the VO-short-name collision domain.
670
+ closure: dict[str, MetaObject] = {}
671
+ _collect_nested_closure(root, payload, closure, {payload.resolution_key()})
672
+ name_map = _assign_nested_names(closure)
673
+
518
674
  # Per-file dedupe set: scoped to this single render call so each emitted
519
- # module is self-contained (no cross-template forward references).
520
- emitted_nested_fqns: set[str] = set()
675
+ # module is self-contained (no cross-template forward references). Keyed by
676
+ # ``resolution_key()`` (see _resolve_object_field_type) so two same-short-name
677
+ # VOs from different packages both emit.
678
+ emitted_nested_keys: set[str] = set()
521
679
  class_name = payload_class_name(template.name)
522
680
  extra_imports: set[str] = set()
523
681
  nested_emit_queue: list[tuple[MetaObject, str]] = []
@@ -541,13 +699,14 @@ def render_payload_vo(
541
699
  payload_vo=payload,
542
700
  root=root,
543
701
  nested_emit_queue=nested_emit_queue,
544
- emitted_nested_fqns=emitted_nested_fqns,
702
+ emitted_nested_keys=emitted_nested_keys,
545
703
  extra_imports=extra_imports,
546
704
  enum_aliases=enum_aliases,
547
705
  docstring=(
548
706
  f"GENERATED payload for template ``{template.name}``.\n\n"
549
707
  f" Field shape derived from the ``{payload.name}`` object.value."
550
708
  ),
709
+ name_map=name_map,
551
710
  )
552
711
  if strict_extras:
553
712
  primary_block = _with_strict_extras(primary_block)
@@ -564,12 +723,13 @@ def render_payload_vo(
564
723
  payload_vo=target,
565
724
  root=root,
566
725
  nested_emit_queue=nested_emit_queue,
567
- emitted_nested_fqns=emitted_nested_fqns,
726
+ emitted_nested_keys=emitted_nested_keys,
568
727
  extra_imports=extra_imports,
569
728
  enum_aliases=enum_aliases,
570
729
  docstring=(
571
730
  f"GENERATED nested payload for collection target ``{target.name}``."
572
731
  ),
732
+ name_map=name_map,
573
733
  )
574
734
  if strict_extras:
575
735
  block = _with_strict_extras(block)
@@ -650,10 +810,11 @@ class PayloadVoGenerator:
650
810
  payload_vo: MetaObject,
651
811
  root: MetaData,
652
812
  nested_emit_queue: list[tuple[MetaObject, str]],
653
- emitted_nested_fqns: set[str],
813
+ emitted_nested_keys: set[str],
654
814
  extra_imports: set[str],
655
815
  enum_aliases: dict[str, str],
656
816
  docstring: str,
817
+ name_map: dict[str, str],
657
818
  ) -> list[str]:
658
819
  """EXTENSION SEAM — the source lines for one Pydantic ``BaseModel`` subclass
659
820
  (primary OR a nested collection target). Defaults to the module-level
@@ -664,10 +825,11 @@ class PayloadVoGenerator:
664
825
  payload_vo,
665
826
  root,
666
827
  nested_emit_queue,
667
- emitted_nested_fqns,
828
+ emitted_nested_keys,
668
829
  extra_imports,
669
830
  enum_aliases,
670
831
  docstring,
832
+ name_map,
671
833
  )
672
834
 
673
835
  def _render_module(self, template: MetaTemplate, root: MetaData) -> str | None:
@@ -133,6 +133,9 @@ class ErrorCode(str, Enum):
133
133
  ERR_PARTIAL_UNRESOLVED = "ERR_PARTIAL_UNRESOLVED"
134
134
  ERR_REQUIRED_SLOT_UNUSED = "ERR_REQUIRED_SLOT_UNUSED"
135
135
  ERR_VAR_NOT_ON_PAYLOAD = "ERR_VAR_NOT_ON_PAYLOAD"
136
+ # Codegen (ADR-0044): two value-object FQNs in one payload artifact derive the same
137
+ # emitted record name even after package-qualification. Peer of ERR_VAR_NOT_ON_PAYLOAD.
138
+ ERR_PAYLOAD_NAME_COLLISION = "ERR_PAYLOAD_NAME_COLLISION"
136
139
  ERR_OUTPUT_TAG_MISSING = "ERR_OUTPUT_TAG_MISSING"
137
140
  # SP-H Unit9 — @filterable: true on a field subtype with no filter-operator
138
141
  # band (e.g. field.object). Would silently generate an empty-ops filter.
@@ -10,7 +10,7 @@
10
10
  { "type": "validator", "subType": "*", "name": "*", "min": 0, "max": null },
11
11
  { "type": "view", "subType": "*", "name": "*", "min": 0, "max": null },
12
12
  { "type": "origin", "subType": "*", "name": "*", "min": 0, "max": null },
13
- { "type": "attr", "subType": "boolean", "name": "required", "min": 0, "max": 1, "description": "When true, the field is NOT NULL. Equivalent to attaching a validator.required child." },
13
+ { "type": "attr", "subType": "boolean", "name": "required", "min": 0, "max": 1, "description": "When true, the field is NOT NULL. Equivalent to attaching a validator.required child. On a non-array string, generated wire-tier input validation (create/patch) additionally rejects the empty string by default — whitespace is accepted — unless an explicit validator.length @min: 0 opts back to presence-only. In-process read models never enforce this at construction." },
14
14
  { "type": "attr", "subType": "boolean", "name": "readOnly", "min": 0, "max": 1, "description": "FR-013: when true, the field is read-only — codegen emits no setter / writable property, the persistence layer skips the column on INSERT/UPDATE, and Zod/Pydantic/class-validator schemas mark it read-only on input variants. The value is populated by the database (computed column, default expression, trigger), by replication, or by another external owner." },
15
15
  { "type": "attr", "name": "default", "min": 0, "max": 1, "description": "Default value applied to the column when no value is supplied. Its type follows the field's own subtype (string / boolean / number / ...). Converted at consumption time via MetaField.defaultValue()." },
16
16
  { "type": "attr", "subType": "boolean", "name": "unique", "min": 0, "max": 1, "description": "When true, the field gets a column-level UNIQUE constraint." }
@@ -13,8 +13,8 @@
13
13
  {
14
14
  "type": "validator",
15
15
  "subType": "required",
16
- "description": "Fails when the value is null/empty (NOT NULL). Equivalent to @required on the owning field.",
17
- "whenToUse": "A field must be present (NOT NULL). Declare it instead of hand-writing a null/empty check."
16
+ "description": "Fails when the value is null (NOT NULL). Equivalent to @required on the owning field: on a non-array string, generated wire-tier input validation also rejects the empty string by default unless an explicit validator.length @min: 0 opts back to presence-only.",
17
+ "whenToUse": "A field must be present (NOT NULL). Declare it instead of hand-writing a presence check."
18
18
  },
19
19
  {
20
20
  "type": "validator",
@@ -47,7 +47,7 @@ def test_gen_nudges_on_stale_manifest(tmp_path, capsys, monkeypatch) -> None:
47
47
  assert rc == 0 # advisory: never changes the exit code
48
48
  err = capsys.readouterr().err
49
49
  assert "0.0.1-old" in err
50
- assert "metaobjects agent-docs" in err
50
+ assert "npx meta agent-docs" in err
51
51
 
52
52
 
53
53
  def test_verify_nudges_on_stale_manifest(tmp_path, capsys, monkeypatch) -> None:
@@ -65,7 +65,7 @@ def test_verify_nudges_on_stale_manifest(tmp_path, capsys, monkeypatch) -> None:
65
65
  assert rc == 0
66
66
  err = capsys.readouterr().err
67
67
  assert "0.0.1-old" in err
68
- assert "metaobjects agent-docs" in err
68
+ assert "npx meta agent-docs" in err
69
69
 
70
70
 
71
71
  def test_gen_silent_when_no_manifest(tmp_path, capsys, monkeypatch) -> None:
@@ -76,7 +76,7 @@ def test_gen_silent_when_no_manifest(tmp_path, capsys, monkeypatch) -> None:
76
76
  rc = main(["gen", meta_dir, "--out", str(out)])
77
77
  assert rc == 0
78
78
  err = capsys.readouterr().err
79
- assert "metaobjects agent-docs" not in err
79
+ assert "npx meta agent-docs" not in err
80
80
 
81
81
 
82
82
  def test_gen_silent_on_corrupt_manifest(tmp_path, capsys, monkeypatch) -> None:
@@ -90,4 +90,4 @@ def test_gen_silent_on_corrupt_manifest(tmp_path, capsys, monkeypatch) -> None:
90
90
  rc = main(["gen", meta_dir, "--out", str(out)])
91
91
  assert rc == 0
92
92
  err = capsys.readouterr().err
93
- assert "metaobjects agent-docs" not in err
93
+ assert "npx meta agent-docs" not in err
@@ -8,10 +8,13 @@ same target).
8
8
  """
9
9
  from __future__ import annotations
10
10
 
11
+ import pytest
12
+
11
13
  import metaobjects.core_types # noqa: F401 — side-effect: registers attr classes
12
14
  from metaobjects.codegen.config import GenConfig
13
15
  from metaobjects.codegen.generator import GenContext
14
16
  from metaobjects.codegen.generators.payload_vo_generator import (
17
+ ERR_PAYLOAD_NAME_COLLISION,
15
18
  PayloadVoGenerator,
16
19
  payload_class_name,
17
20
  payload_module_name,
@@ -428,6 +431,92 @@ def test_plain_object_field_strips_fqn_to_bare_nested_payload() -> None:
428
431
  assert "::" not in code
429
432
 
430
433
 
434
+ def test_cross_package_short_name_collision_qualifies_both_nested_classes() -> None:
435
+ """ADR-0044 — two ``object.value`` ``Note``s in different packages, both reachable
436
+ from one payload by FQN ``@objectRef``, emit as TWO distinct package-qualified
437
+ classes (``AcmeAlphaNotePayload`` / ``AcmeBetaNotePayload``) — never one shadowed
438
+ ``NotePayload`` that drops the second shape. The un-colliding ``Digest`` primary
439
+ stays bare (template-named)."""
440
+ alpha = _value_object("Note", [_field("alphaText", fc.FIELD_SUBTYPE_STRING)], package="acme::alpha")
441
+ beta = _value_object("Note", [_field("betaText", fc.FIELD_SUBTYPE_STRING)], package="acme::beta")
442
+ digest = _value_object(
443
+ "Digest",
444
+ [
445
+ _object_field("fromAlpha", "acme::alpha::Note"),
446
+ _object_field("fromBeta", "acme::beta::Note"),
447
+ ],
448
+ package="acme::app",
449
+ )
450
+ tmpl = _template("DigestOut", "Digest")
451
+ root = _root([alpha, beta, digest, tmpl])
452
+ out = render_payload_vo(tmpl, root)
453
+ assert out is not None
454
+ assert "class AcmeAlphaNotePayload(BaseModel):" in out
455
+ assert "class AcmeBetaNotePayload(BaseModel):" in out
456
+ assert "class NotePayload(BaseModel):" not in out
457
+ assert "fromAlpha: AcmeAlphaNotePayload | None = None" in out
458
+ assert "fromBeta: AcmeBetaNotePayload | None = None" in out
459
+ assert '__all__ = ["DigestOutPayload", "AcmeAlphaNotePayload", "AcmeBetaNotePayload"]' in out
460
+
461
+
462
+ def test_nested_of_nested_collision_qualifies_across_depths() -> None:
463
+ """ADR-0044 — the collision domain is the WHOLE transitive closure, not one
464
+ depth. A ``Note`` reached at depth 2 (via ``Outer.inner``) collides with a
465
+ ``Note`` reached at depth 1; both qualify (``AcmeBetaNotePayload`` /
466
+ ``AcmeGammaNotePayload``) while the un-colliding intermediate ``Outer`` stays
467
+ bare. Guards the recursive `_collect_nested_closure` walk."""
468
+ beta_note = _value_object("Note", [_field("betaText", fc.FIELD_SUBTYPE_STRING)], package="acme::beta")
469
+ gamma_note = _value_object("Note", [_field("gammaText", fc.FIELD_SUBTYPE_STRING)], package="acme::gamma")
470
+ outer = _value_object(
471
+ "Outer", [_object_field("inner", "acme::beta::Note")], package="acme::alpha"
472
+ )
473
+ digest = _value_object(
474
+ "Digest",
475
+ [
476
+ _object_field("fromOuter", "acme::alpha::Outer"),
477
+ _object_field("fromGamma", "acme::gamma::Note"),
478
+ ],
479
+ package="acme::app",
480
+ )
481
+ tmpl = _template("DigestOut", "Digest")
482
+ root = _root([beta_note, gamma_note, outer, digest, tmpl])
483
+ out = render_payload_vo(tmpl, root)
484
+ assert out is not None
485
+ assert "class OuterPayload(BaseModel):" in out # unique → bare
486
+ assert "class AcmeBetaNotePayload(BaseModel):" in out # depth-2 collision member
487
+ assert "class AcmeGammaNotePayload(BaseModel):" in out # depth-1 collision member
488
+ assert "class NotePayload(BaseModel):" not in out
489
+ assert "inner: AcmeBetaNotePayload | None = None" in out
490
+ assert "fromGamma: AcmeGammaNotePayload | None = None" in out
491
+
492
+
493
+ def test_derived_name_still_colliding_fails_loud() -> None:
494
+ """ADR-0044 §4 backstop — two DISTINCT package FQNs whose package-qualified derived
495
+ names still collide (``acme::alpha`` and ``acmeAlpha`` both PascalCase-fold to
496
+ ``AcmeAlpha``) must fail loud with ``ERR_PAYLOAD_NAME_COLLISION``, never silently
497
+ emit one class twice."""
498
+ a = _value_object("Note", [_field("a", fc.FIELD_SUBTYPE_STRING)], package="acme::alpha")
499
+ b = _value_object("Note", [_field("b", fc.FIELD_SUBTYPE_STRING)], package="acmeAlpha")
500
+ digest = _value_object(
501
+ "Digest",
502
+ [
503
+ _object_field("fromA", "acme::alpha::Note"),
504
+ _object_field("fromB", "acmeAlpha::Note"),
505
+ ],
506
+ package="acme::app",
507
+ )
508
+ tmpl = _template("DigestOut", "Digest")
509
+ root = _root([a, b, digest, tmpl])
510
+ with pytest.raises(ValueError) as ei:
511
+ render_payload_vo(tmpl, root)
512
+ msg = str(ei.value)
513
+ assert ERR_PAYLOAD_NAME_COLLISION in msg
514
+ assert "AcmeAlphaNotePayload" in msg
515
+ # Both source FQNs are named so the author can find and rename one.
516
+ assert "acme::alpha::Note" in msg
517
+ assert "acmeAlpha::Note" in msg
518
+
519
+
431
520
  # ---------------------------------------------------------------------------
432
521
  # Resolution edge cases.
433
522
  # ---------------------------------------------------------------------------