metaobjects 0.19.2__tar.gz → 0.19.4__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 (391) hide show
  1. {metaobjects-0.19.2 → metaobjects-0.19.4}/PKG-INFO +1 -1
  2. {metaobjects-0.19.2 → metaobjects-0.19.4}/pyproject.toml +1 -1
  3. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/cli.py +26 -4
  4. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/payload_vo_generator.py +196 -34
  5. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/router_generator.py +133 -1
  6. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/errors.py +3 -0
  7. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_cli_verify_subverbs.py +110 -0
  8. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_payload_vo_generator.py +89 -0
  9. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_render_helper_conformance.py +43 -1
  10. metaobjects-0.19.4/tests/codegen/test_router_autoset.py +168 -0
  11. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/api_contract_assertions.py +42 -0
  12. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/api_contract_server.py +72 -17
  13. {metaobjects-0.19.2 → metaobjects-0.19.4}/.gitignore +0 -0
  14. {metaobjects-0.19.2 → metaobjects-0.19.4}/LICENSE +0 -0
  15. {metaobjects-0.19.2 → metaobjects-0.19.4}/README.md +0 -0
  16. {metaobjects-0.19.2 → metaobjects-0.19.4}/hatch_build.py +0 -0
  17. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/__init__.py +0 -0
  18. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/agent_context/__init__.py +0 -0
  19. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/agent_context/scaffold.py +0 -0
  20. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/apidocs/__init__.py +0 -0
  21. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/apidocs/api_model.py +0 -0
  22. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/apidocs/builder.py +0 -0
  23. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/apidocs/naming.py +0 -0
  24. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/apidocs/paths.py +0 -0
  25. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/apidocs/renderer.py +0 -0
  26. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/attr_class_map.py +0 -0
  27. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  28. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/__init__.py +0 -0
  29. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/config.py +0 -0
  30. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/constants.py +0 -0
  31. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  32. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  33. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/format.py +0 -0
  34. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  35. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generator.py +0 -0
  36. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generator_registry.py +0 -0
  37. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/__init__.py +0 -0
  38. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  39. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  40. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  41. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  42. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  43. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  44. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  45. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  46. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  47. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  48. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  49. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  50. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  51. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  52. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/runner.py +0 -0
  53. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  54. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  55. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  56. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  57. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  58. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  59. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/codegen/type_map.py +0 -0
  60. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/core_types.py +0 -0
  61. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/datatype.py +0 -0
  62. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/documentation/__init__.py +0 -0
  63. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/documentation/doc_constants.py +0 -0
  64. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/documentation/doc_provider.py +0 -0
  65. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/documentation/doc_schema.py +0 -0
  66. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/__init__.py +0 -0
  67. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/merge.py +0 -0
  68. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/meta_data_loader.py +0 -0
  69. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/registered_validation.py +0 -0
  70. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/sources/__init__.py +0 -0
  71. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/sources/directory_source.py +0 -0
  72. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/sources/file_source.py +0 -0
  73. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  74. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/sources/uri_source.py +0 -0
  75. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/validate_discriminator.py +0 -0
  76. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/validate_field_readonly.py +0 -0
  77. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  78. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  79. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  80. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/loader/validation_passes.py +0 -0
  81. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/__init__.py +0 -0
  82. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/__init__.py +0 -0
  83. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  84. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  85. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  86. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/field/__init__.py +0 -0
  87. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  88. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  89. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  90. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  91. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  92. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/index/__init__.py +0 -0
  93. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  94. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  95. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/object/__init__.py +0 -0
  96. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  97. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  98. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  99. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  100. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  101. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/object/value_object.py +0 -0
  102. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  103. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  104. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  105. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  106. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  107. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  108. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/meta_data.py +0 -0
  109. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/meta_root.py +0 -0
  110. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/__init__.py +0 -0
  111. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  112. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  113. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  114. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  115. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  116. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  117. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  118. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  119. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  120. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/__init__.py +0 -0
  121. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  122. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  123. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  124. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  125. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  126. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  127. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  128. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  129. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/provider_extends.py +0 -0
  130. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/template/__init__.py +0 -0
  131. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/template/meta_template.py +0 -0
  132. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  133. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/meta/template/template_constants.py +0 -0
  134. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/naming_refs.py +0 -0
  135. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/parser.py +0 -0
  136. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/parser_yaml.py +0 -0
  137. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/provider.py +0 -0
  138. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/py.typed +0 -0
  139. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/registry.py +0 -0
  140. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/registry_manifest.py +0 -0
  141. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/__init__.py +0 -0
  142. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/email_document.py +0 -0
  143. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/escapers.py +0 -0
  144. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  145. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/__init__.py +0 -0
  146. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/coerce.py +0 -0
  147. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/extract.py +0 -0
  148. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/extract_map.py +0 -0
  149. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  150. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/locate.py +0 -0
  151. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/normalize.py +0 -0
  152. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/strip.py +0 -0
  153. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/types.py +0 -0
  154. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  155. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/filesystem_provider.py +0 -0
  156. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/prompt/__init__.py +0 -0
  157. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  158. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  159. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  160. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  161. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  162. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/renderer.py +0 -0
  163. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/render/verify.py +0 -0
  164. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/runtime/__init__.py +0 -0
  165. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/runtime/llm_recorder.py +0 -0
  166. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  167. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/runtime/object_manager.py +0 -0
  168. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/runtime/tph.py +0 -0
  169. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/serializer_json.py +0 -0
  170. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/shared/__init__.py +0 -0
  171. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/shared/base_types.py +0 -0
  172. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/shared/separators.py +0 -0
  173. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/shared/structural.py +0 -0
  174. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/source/__init__.py +0 -0
  175. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/source/error_source.py +0 -0
  176. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/source/json_path.py +0 -0
  177. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/source/semantic_diff.py +0 -0
  178. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/source/yaml_positions.py +0 -0
  179. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  180. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/attr.json +0 -0
  181. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/db.json +0 -0
  182. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  183. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/field.json +0 -0
  184. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/identity.json +0 -0
  185. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/index.json +0 -0
  186. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/layout.json +0 -0
  187. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/object.json +0 -0
  188. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/origin.json +0 -0
  189. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  190. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  191. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/source.json +0 -0
  192. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/template.json +0 -0
  193. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  194. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/ui.json +0 -0
  195. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/validator.json +0 -0
  196. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/spec_metamodel/view.json +0 -0
  197. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/super_resolve.py +0 -0
  198. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/validation_types.py +0 -0
  199. {metaobjects-0.19.2 → metaobjects-0.19.4}/src/metaobjects/yaml_desugar.py +0 -0
  200. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/__init__.py +0 -0
  201. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/__init__.py +0 -0
  202. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  203. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/extends/expected/Program.py +0 -0
  204. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/extends/meta.json +0 -0
  205. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  206. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  207. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/nested-array/meta.json +0 -0
  208. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  209. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  210. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/scalars/meta.json +0 -0
  211. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  212. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/golden/vanilla/meta.json +0 -0
  213. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_abstract_conformance.py +0 -0
  214. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_api_docs_builder.py +0 -0
  215. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_api_docs_paths.py +0 -0
  216. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_cli.py +0 -0
  217. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_cli_providers.py +0 -0
  218. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_cli_registry.py +0 -0
  219. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  220. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_cli_verify_strict.py +0 -0
  221. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_constants_config.py +0 -0
  222. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_entity_model.py +0 -0
  223. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_enum_conformance.py +0 -0
  224. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  225. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_extractor_generator.py +0 -0
  226. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  227. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_format.py +0 -0
  228. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  229. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_fr010_output_codegen.py +0 -0
  230. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  231. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_generator.py +0 -0
  232. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_generator_extension_seams.py +0 -0
  233. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_golden.py +0 -0
  234. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  235. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_inheritance_conformance.py +0 -0
  236. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_instance_artifacts.py +0 -0
  237. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_m2m_codegen.py +0 -0
  238. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_origin_nullability.py +0 -0
  239. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_output_parser_generator.py +0 -0
  240. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_overwrite_policy.py +0 -0
  241. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_projection_compile.py +0 -0
  242. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_render_helper_generator.py +0 -0
  243. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_reverse_finders.py +0 -0
  244. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_router_generator.py +0 -0
  245. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_runner.py +0 -0
  246. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_template_data.py +0 -0
  247. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_template_generator.py +0 -0
  248. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_template_output_pattern.py +0 -0
  249. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_template_scope_helpers.py +0 -0
  250. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_template_scope_walk.py +0 -0
  251. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_template_spec.py +0 -0
  252. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_tph_codegen.py +0 -0
  253. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_trace_helper_generator.py +0 -0
  254. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_type_map.py +0 -0
  255. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_validation_conformance.py +0 -0
  256. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/codegen/test_write_through.py +0 -0
  257. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/__init__.py +0 -0
  258. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/capabilities.py +0 -0
  259. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/conformance-expected-failures.json +0 -0
  260. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/conformance_adapter.py +0 -0
  261. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/corpus.py +0 -0
  262. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/expected_failures.py +0 -0
  263. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/fixture_discovery.py +0 -0
  264. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/navigator.py +0 -0
  265. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  266. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_conformance.py +0 -0
  267. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_extract_conformance.py +0 -0
  268. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_extract_object_verdict.py +0 -0
  269. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  270. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_fr011_attrs.py +0 -0
  271. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_generator_registry_conformance.py +0 -0
  272. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_object_model_conformance.py +0 -0
  273. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_provider_composition_conformance.py +0 -0
  274. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_registry_conformance.py +0 -0
  275. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_runner_hardfail.py +0 -0
  276. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  277. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_strict_attr_load.py +0 -0
  278. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_template_codegen_conformance.py +0 -0
  279. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_template_generator_conformance.py +0 -0
  280. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/test_yaml_conformance.py +0 -0
  281. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  282. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/__init__.py +0 -0
  283. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/api_contract_jsonb_server.py +0 -0
  284. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/api_contract_m2m_server.py +0 -0
  285. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/generated_jsonb_app.py +0 -0
  286. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/generated_m2m_app.py +0 -0
  287. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/generated_router_app.py +0 -0
  288. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/generated_tph_app.py +0 -0
  289. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/meta_ai_trace.yaml +0 -0
  290. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/normalization.py +0 -0
  291. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/postgres_container.py +0 -0
  292. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/query_runner.py +0 -0
  293. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/scenarios.py +0 -0
  294. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_api_contract.py +0 -0
  295. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_api_contract_generated.py +0 -0
  296. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_api_contract_jsonb.py +0 -0
  297. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_api_contract_m2m.py +0 -0
  298. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  299. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_api_contract_tph_generated.py +0 -0
  300. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  301. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_llm_call_trace.py +0 -0
  302. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_normalization.py +0 -0
  303. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_query_scenarios.py +0 -0
  304. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/integration/test_runtime_return_types.py +0 -0
  305. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/open_closed_proof_test.py +0 -0
  306. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/__init__.py +0 -0
  307. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/__init__.py +0 -0
  308. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_coerce.py +0 -0
  309. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_extract.py +0 -0
  310. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_extract_map.py +0 -0
  311. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  312. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_locate.py +0 -0
  313. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_model.py +0 -0
  314. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_normalize.py +0 -0
  315. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_strip.py +0 -0
  316. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  317. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/prompt/__init__.py +0 -0
  318. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/prompt/test_output_format_renderer.py +0 -0
  319. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/test_email_document.py +0 -0
  320. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/test_filesystem_provider.py +0 -0
  321. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/test_output_format_renderer_nested.py +0 -0
  322. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/test_output_prompt_conformance.py +0 -0
  323. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/test_render_conformance.py +0 -0
  324. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/test_render_max_chars.py +0 -0
  325. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/test_verify.py +0 -0
  326. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/render/test_verify_conformance.py +0 -0
  327. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/source/__init__.py +0 -0
  328. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/source/test_fr5c_merge_attribution.py +0 -0
  329. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/source/test_fr5d_reference_resolution.py +0 -0
  330. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/source/test_fr5e_database_source_shape.py +0 -0
  331. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/source/test_json_path.py +0 -0
  332. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/source/test_semantic_diff.py +0 -0
  333. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/source/test_source_on_node.py +0 -0
  334. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/source/test_yaml_positions.py +0 -0
  335. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/test_api_docs_accuracy.py +0 -0
  336. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/__init__.py +0 -0
  337. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_agent_context_staleness.py +0 -0
  338. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_capabilities.py +0 -0
  339. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_common_attrs.py +0 -0
  340. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_core_types.py +0 -0
  341. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_effective_package.py +0 -0
  342. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_errors.py +0 -0
  343. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_field_enum.py +0 -0
  344. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_field_map_validation.py +0 -0
  345. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  346. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  347. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_index_lookup.py +0 -0
  348. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_llm_recorder.py +0 -0
  349. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_loader.py +0 -0
  350. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_loader_bom.py +0 -0
  351. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_loader_class.py +0 -0
  352. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_merge.py +0 -0
  353. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_meta_attr.py +0 -0
  354. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_meta_data.py +0 -0
  355. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_meta_source.py +0 -0
  356. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_module_shortcuts.py +0 -0
  357. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_n2m_resolver.py +0 -0
  358. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  359. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  360. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  361. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_object_manager_write_through.py +0 -0
  362. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_one_primary_source.py +0 -0
  363. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_parser.py +0 -0
  364. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_parser_yaml.py +0 -0
  365. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_provider.py +0 -0
  366. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_provider_extension.py +0 -0
  367. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_registry.py +0 -0
  368. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_registry_completeness.py +0 -0
  369. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_registry_extend.py +0 -0
  370. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_registry_sealed.py +0 -0
  371. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_relationship_referential_actions.py +0 -0
  372. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_resolution_key.py +0 -0
  373. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  374. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_serializer.py +0 -0
  375. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_shared_constants.py +0 -0
  376. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_smoke.py +0 -0
  377. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_source_escapes.py +0 -0
  378. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_sources.py +0 -0
  379. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_strict_child_placement.py +0 -0
  380. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_super_resolve.py +0 -0
  381. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_template_toolcall.py +0 -0
  382. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  383. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_validation_attr_schema.py +0 -0
  384. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_validation_filter_values.py +0 -0
  385. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_validation_origin_195.py +0 -0
  386. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_validation_origin_paths.py +0 -0
  387. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_validation_sort_field.py +0 -0
  388. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_validation_warnings.py +0 -0
  389. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_write_coercion_arrays.py +0 -0
  390. {metaobjects-0.19.2 → metaobjects-0.19.4}/tests/unit/test_yaml_desugar.py +0 -0
  391. {metaobjects-0.19.2 → metaobjects-0.19.4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaobjects
3
- Version: 0.19.2
3
+ Version: 0.19.4
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.2"
3
+ version = "0.19.4"
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"
@@ -573,6 +573,16 @@ _TEMPLATE_TEXT_REF_ATTRS = (
573
573
  )
574
574
 
575
575
 
576
+ def _attr_str_list(raw: object) -> list[str] | None:
577
+ """Coerce a string-array attr (a list, or a single string) to ``list[str]``; ``None``
578
+ when absent — the shape :func:`render.verify` expects for required_slots/required_tags."""
579
+ if isinstance(raw, str):
580
+ return [raw]
581
+ if isinstance(raw, (list, tuple)):
582
+ return [str(x) for x in raw]
583
+ return None
584
+
585
+
576
586
  def _verify_templates(args: argparse.Namespace) -> int:
577
587
  """``verify --templates`` — the template/prompt ``{{field}}`` ↔ payload drift gate.
578
588
 
@@ -659,6 +669,16 @@ def _verify_templates(args: argparse.Namespace) -> int:
659
669
  error_count += 1
660
670
  continue
661
671
 
672
+ # #193/#230: @requiredSlots/@requiredTags are a template.prompt concept — enforced ONLY on
673
+ # the prompt path (the email/document output gate does not apply them per-part; the #193
674
+ # cross-port consensus). A prompt whose body omits a required output tag →
675
+ # ERR_OUTPUT_TAG_MISSING (drift, fails); an unused required slot → ERR_REQUIRED_SLOT_UNUSED
676
+ # (warning). This brings Python to parity with TS/Java/C# (previously it enforced neither).
677
+ is_prompt = tmpl.sub_type == tc.TEMPLATE_SUBTYPE_PROMPT
678
+ # ADR-0039: resolving read — an inherited @requiredSlots/@requiredTags is honored.
679
+ req_slots = _attr_str_list(tmpl.get_meta_attr(tc.TEMPLATE_ATTR_REQUIRED_SLOTS)) if is_prompt else None
680
+ req_tags = _attr_str_list(tmpl.get_meta_attr(tc.TEMPLATE_ATTR_REQUIRED_TAGS)) if is_prompt else None
681
+
662
682
  for ref in refs:
663
683
  text = provider.resolve(ref)
664
684
  if text is None:
@@ -670,12 +690,14 @@ def _verify_templates(args: argparse.Namespace) -> int:
670
690
  error_count += 1
671
691
  continue
672
692
  checked += 1
673
- for e in render_verify(text, fields, provider=provider):
693
+ for e in render_verify(
694
+ text, fields, provider=provider,
695
+ required_slots=req_slots, required_tags=req_tags,
696
+ ):
674
697
  if e.code == ERR_REQUIRED_SLOT_UNUSED:
675
- continue # warning, not drift
698
+ continue # an unused required slot is a warning, not drift
676
699
  print(
677
- f"error: [{tmpl.name}] ref '{ref}' — {e.code}: "
678
- f"{{{{{e.path}}}}} not on payload VO '{payload_ref}'.",
700
+ f"error: [{tmpl.name}] ref '{ref}' — {e.code}: {e.path}",
679
701
  file=sys.stderr,
680
702
  )
681
703
  error_count += 1
@@ -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:
@@ -52,6 +52,7 @@ from metaobjects.codegen.instance_artifacts import emits_instance_artifacts
52
52
  from metaobjects.codegen.type_map import PyType, py_type_for
53
53
  from metaobjects.meta.core.field import field_constants as fc
54
54
  from metaobjects.meta.core.field.meta_field import MetaField
55
+ from metaobjects.meta.persistence.db import db_constants as dbc
55
56
  from metaobjects.meta.core.identity.identity_constants import (
56
57
  IDENTITY_ATTR_FIELDS,
57
58
  IDENTITY_REFERENCE_ATTR_REFERENCES,
@@ -95,7 +96,73 @@ def _required_field_names(entity: MetaObject) -> list[str]:
95
96
  _scalar_fields, which drops object fields for the sort allowlist). FR-035
96
97
  PATCH-2 guards present-null on any of these, and a @required jsonb column can
97
98
  be nulled just like a scalar one."""
98
- return [f.name for f in entity.fields() if is_required(f)]
99
+ # #203/ADR-0045: an @autoSet field is server-owned — never a caller-facing PATCH field, so
100
+ # it is not present-null-checked (a caller-sent null is ignored/stamped, not a 400). Mirrors
101
+ # the Kotlin controller (which excludes @autoSet from its patch-settable set).
102
+ return [f.name for f in entity.fields() if is_required(f) and not _is_auto_set(f)]
103
+
104
+
105
+ # --- #203 / ADR-0045: @autoSet stamping in the generated router (above the repo seam) ----------
106
+
107
+ _AUTO_SET_TEMPORAL_SUBTYPES = (
108
+ fc.FIELD_SUBTYPE_DATE,
109
+ fc.FIELD_SUBTYPE_TIME,
110
+ fc.FIELD_SUBTYPE_TIMESTAMP,
111
+ )
112
+
113
+
114
+ def _is_auto_set(field: MetaField) -> bool:
115
+ """True iff *field* is a server-owned ``@autoSet`` column (ADR-0039 resolving read)."""
116
+ return field.get_meta_attr(fc.FIELD_ATTR_AUTO_SET) in (
117
+ fc.AUTO_SET_ON_CREATE,
118
+ fc.AUTO_SET_ON_UPDATE,
119
+ )
120
+
121
+
122
+ def _auto_set_split(entity: MetaObject) -> tuple[list[MetaField], list[MetaField]]:
123
+ """The entity's ``@autoSet`` temporal fields split into ``(onCreate, onUpdate)``.
124
+
125
+ Reads the RESOLVING ``@autoSet`` (ADR-0039) so an inherited
126
+ ``BaseEntity.createdAt/updatedAt`` is honored; a non-temporal field is skipped.
127
+ Both lists are empty for an entity that declares none, keeping its router
128
+ byte-identical to the pre-#203 output."""
129
+ on_create: list[MetaField] = []
130
+ on_update: list[MetaField] = []
131
+ for f in entity.fields():
132
+ if f.sub_type not in _AUTO_SET_TEMPORAL_SUBTYPES:
133
+ continue
134
+ mode = f.get_meta_attr(fc.FIELD_ATTR_AUTO_SET) # ADR-0039 resolving: may be inherited via extends
135
+ if mode == fc.AUTO_SET_ON_CREATE:
136
+ on_create.append(f)
137
+ elif mode == fc.AUTO_SET_ON_UPDATE:
138
+ on_update.append(f)
139
+ return on_create, on_update
140
+
141
+
142
+ def _auto_set_stamp_expr(field: MetaField, base_var: str) -> str:
143
+ """The inline now()-expression for an ``@autoSet`` *field*, derived from one shared
144
+ tz-aware *base_var* datetime so every column stamped in one op is equal (a fresh
145
+ row's created == updated). Keyed off the COLUMN's temporal type: ``field.date`` →
146
+ ``.date()``; ``field.time`` → ``.time()``; ``field.timestamp`` → the tz-aware base
147
+ (``@localTime`` → a naive wall-clock value). Mirrors ObjectManager._auto_set_stamp."""
148
+ sub = field.sub_type
149
+ if sub == fc.FIELD_SUBTYPE_DATE:
150
+ return f"{base_var}.date()"
151
+ if sub == fc.FIELD_SUBTYPE_TIME:
152
+ return f"{base_var}.time()"
153
+ is_tz = field.get_meta_attr(dbc.FIELD_ATTR_LOCAL_TIME) is not True # ADR-0039 resolving
154
+ return base_var if is_tz else f"{base_var}.replace(tzinfo=None)"
155
+
156
+
157
+ def _auto_set_stamp_lines(fields: list[MetaField], comment: str) -> list[str]:
158
+ """Router-handler body lines stamping *fields* into ``dto`` from ONE captured base
159
+ instant (so all same-op columns are equal), led by *comment*. Empty for no fields
160
+ (so the call site spreads to nothing — a non-@autoSet router stays byte-identical)."""
161
+ if not fields:
162
+ return []
163
+ lines = [f" # {comment}", " _asnow = _dt.datetime.now(_dt.timezone.utc)"]
164
+ lines += [f' dto["{f.name}"] = {_auto_set_stamp_expr(f, "_asnow")}' for f in fields]
165
+ return lines
99
166
 
100
167
 
101
168
  def _py_set_literal(names: list[str], *, frozen: bool = False) -> str:
@@ -259,6 +326,8 @@ class RouterGenerator:
259
326
  ops_const: str,
260
327
  model_name: str,
261
328
  patch_model: str,
329
+ create_autoset: list[str] = (),
330
+ update_autoset: list[str] = (),
262
331
  ) -> list[str]:
263
332
  """One CRUD route handler block, dispatched by *name*
264
333
  (``list`` / ``get`` / ``create`` / ``update`` / ``delete``). Override to
@@ -318,6 +387,10 @@ class RouterGenerator:
318
387
  f" {model_name}(**dto)",
319
388
  " except ValidationError:",
320
389
  ' return JSONResponse(status_code=400, content={"error": "validation"})',
390
+ # #203/ADR-0045: the generated router (the API surface) stamps @autoSet columns
391
+ # ABOVE the consumer repo seam — onCreate + onUpdate both stamped from one instant
392
+ # (a fresh row's createdAt == updatedAt), ignoring any caller-supplied value.
393
+ *create_autoset,
321
394
  " return repo.create(dto)",
322
395
  ]
323
396
  if name == "update":
@@ -341,6 +414,10 @@ class RouterGenerator:
341
414
  f" {patch_model}(**dto)",
342
415
  " except ValidationError:",
343
416
  ' return JSONResponse(status_code=400, content={"error": "validation"})',
417
+ # #203/ADR-0045: bump onUpdate @autoSet columns (server-owned) on EVERY patch,
418
+ # above the repo seam — a server-inserted present key (compatible with the PATCH
419
+ # present-key tristate); onCreate columns are never touched here.
420
+ *update_autoset,
344
421
  f" saved = repo.update({pk_param}, dto)",
345
422
  " if saved is None:",
346
423
  ' return JSONResponse(status_code=404, content={"error": "not_found"})',
@@ -417,6 +494,24 @@ class RouterGenerator:
417
494
  ops_const = f"{upper}_FILTER_OPS_BY_FIELD"
418
495
  allowlist_module = f"{snake}_filter_allowlist"
419
496
 
497
+ # #203/ADR-0045: @autoSet columns live on the BASE entity and are shared by every
498
+ # subtype (RESOLVING _auto_set_split(entity) picks up an inherited BaseEntity
499
+ # column too) — computed once here and threaded into each per-subtype handler
500
+ # below (empty for a non-@autoSet hierarchy → byte-identical output).
501
+ on_create_auto, on_update_auto = _auto_set_split(entity)
502
+ has_autoset = bool(on_create_auto or on_update_auto)
503
+ create_autoset = _auto_set_stamp_lines(
504
+ on_create_auto + on_update_auto,
505
+ "#203/ADR-0045: stamp @autoSet columns (server-owned; caller ignored).",
506
+ )
507
+ update_autoset = [
508
+ f' dto.pop("{f.name}", None) # onCreate @autoSet is write-once (server-owned)'
509
+ for f in on_create_auto
510
+ ] + _auto_set_stamp_lines(
511
+ on_update_auto,
512
+ "#203/ADR-0045: bump onUpdate @autoSet column(s) (server-owned).",
513
+ )
514
+
420
515
  # Sort allowlist = base scalar fields ∪ every subtype's own scalar fields, so a
421
516
  # per-subtype list can sort on a subtype-only column too. Stable order.
422
517
  sort_fields: list[str] = [f.name for f in _scalar_fields(entity)]
@@ -448,6 +543,10 @@ class RouterGenerator:
448
543
  )
449
544
  parts.append("from __future__ import annotations")
450
545
  parts.append("")
546
+ if has_autoset:
547
+ # #203/ADR-0045: the router stamps @autoSet columns inline (no runtime dependency).
548
+ parts.append("import datetime as _dt")
549
+ parts.append("")
451
550
  for import_line in sorted(pk.imports):
452
551
  parts.append(import_line)
453
552
  if pk.imports:
@@ -555,6 +654,9 @@ class RouterGenerator:
555
654
  parts.append(f" {sub}Create(**dto)")
556
655
  parts.append(" except ValidationError:")
557
656
  parts.append(' return JSONResponse(status_code=400, content={"error": "validation"})')
657
+ # #203/ADR-0045: stamp @autoSet columns ABOVE the repo seam (empty for a
658
+ # non-@autoSet hierarchy — byte-identical output).
659
+ parts.extend(create_autoset)
558
660
  parts.append(f' return repo.create("{val}", dto)')
559
661
  parts.append("")
560
662
  parts.append("")
@@ -585,6 +687,9 @@ class RouterGenerator:
585
687
  parts.append(f" {sub}Patch(**dto)")
586
688
  parts.append(" except ValidationError:")
587
689
  parts.append(' return JSONResponse(status_code=400, content={"error": "validation"})')
690
+ # #203/ADR-0045: pop the write-once onCreate column(s), bump onUpdate — ABOVE
691
+ # the repo seam (empty for a non-@autoSet hierarchy — byte-identical output).
692
+ parts.extend(update_autoset)
588
693
  parts.append(f' saved = repo.update("{val}", {pk_param}, dto)')
589
694
  parts.append(" if saved is None:")
590
695
  parts.append(' return JSONResponse(status_code=404, content={"error": "not_found"})')
@@ -684,6 +789,27 @@ class RouterGenerator:
684
789
  # is a 400 — the update handler guards these before the repo call.
685
790
  required_set_body = _py_set_literal(_required_field_names(entity), frozen=True)
686
791
 
792
+ # #203/ADR-0045: @autoSet stamp lines for the create/update handlers (empty for a
793
+ # non-@autoSet entity → byte-identical output). insert stamps onCreate+onUpdate from one
794
+ # instant; a patch bumps onUpdate only (createdAt stays immutable).
795
+ _on_create_auto, _on_update_auto = _auto_set_split(entity)
796
+ create_autoset = _auto_set_stamp_lines(
797
+ _on_create_auto + _on_update_auto,
798
+ "#203/ADR-0045: stamp @autoSet columns (server-owned; caller ignored).",
799
+ )
800
+ # #203/ADR-0045 (integrity): onCreate @autoSet columns are write-once — strip any
801
+ # caller-supplied value on PATCH before the repo call (a caller cannot mutate createdAt via
802
+ # the deployed API). Mirrors ObjectManager.update (`data.pop(...)`) + the Kotlin controller
803
+ # (which excludes @autoSet from its patch-settable set).
804
+ update_autoset = [
805
+ f' dto.pop("{f.name}", None) # onCreate @autoSet is write-once (server-owned)'
806
+ for f in _on_create_auto
807
+ ] + _auto_set_stamp_lines(
808
+ _on_update_auto,
809
+ "#203/ADR-0045: bump onUpdate @autoSet column(s) (server-owned).",
810
+ )
811
+ has_autoset = bool(_on_create_auto or _on_update_auto)
812
+
687
813
  parts: list[str] = []
688
814
  parts.append(
689
815
  generated_header(short_name, _effective_fqn(entity)).rstrip() + "\n"
@@ -691,6 +817,10 @@ class RouterGenerator:
691
817
  )
692
818
  parts.append("from __future__ import annotations")
693
819
  parts.append("")
820
+ if has_autoset:
821
+ # #203/ADR-0045: the router stamps @autoSet columns inline (no runtime dependency).
822
+ parts.append("import datetime as _dt")
823
+ parts.append("")
694
824
  for import_line in sorted(pk.imports):
695
825
  parts.append(import_line)
696
826
  if pk.imports:
@@ -762,6 +892,8 @@ class RouterGenerator:
762
892
  ops_const=ops_const,
763
893
  model_name=f"{short_name}Create",
764
894
  patch_model=f"{short_name}Patch",
895
+ create_autoset=create_autoset,
896
+ update_autoset=update_autoset,
765
897
  )
766
898
  for i, hname in enumerate(("list", "get", "create", "update", "delete")):
767
899
  if i > 0:
@@ -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.