metaobjects 0.14.0__tar.gz → 0.15.0__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 (373) hide show
  1. {metaobjects-0.14.0 → metaobjects-0.15.0}/PKG-INFO +1 -1
  2. {metaobjects-0.14.0 → metaobjects-0.15.0}/pyproject.toml +1 -1
  3. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/builder.py +6 -4
  4. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/naming.py +75 -0
  5. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/cli.py +4 -2
  6. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/extract_delegate_emitter.py +6 -1
  7. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/fr010_field_mapping.py +21 -13
  8. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/entity_model.py +40 -0
  9. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/extractor_generator.py +3 -2
  10. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +15 -3
  11. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +9 -3
  12. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/m2m_codegen.py +12 -5
  13. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/output_parser_generator.py +5 -2
  14. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +3 -2
  15. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +14 -6
  16. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/render_helper_generator.py +11 -8
  17. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/router_generator.py +84 -4
  18. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/tph_plan.py +13 -5
  19. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +8 -2
  20. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/output_format_spec_emitter.py +2 -2
  21. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/runner.py +2 -0
  22. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/template_codegen/template_data.py +22 -14
  23. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/type_map.py +14 -4
  24. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/core_types.py +21 -0
  25. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/merge.py +5 -0
  26. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/registered_validation.py +10 -1
  27. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/validate_discriminator.py +15 -2
  28. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/validate_field_readonly.py +19 -4
  29. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/validate_source_parameter_ref.py +23 -6
  30. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/validate_source_physical_names.py +3 -0
  31. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/validation_passes.py +189 -62
  32. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/field/field_constants.py +27 -0
  33. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/field/meta_field.py +11 -1
  34. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/meta_object.py +6 -1
  35. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/object_extract.py +35 -21
  36. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +9 -4
  37. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +14 -10
  38. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/meta_data.py +46 -1
  39. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/db/db_constants.py +24 -11
  40. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/db/db_provider.py +32 -6
  41. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/source/meta_source.py +14 -8
  42. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/template/meta_template.py +12 -7
  43. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/parser.py +6 -0
  44. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/registry_manifest.py +27 -13
  45. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/runtime/n2m_resolver.py +5 -1
  46. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/runtime/object_manager.py +33 -13
  47. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/runtime/tph.py +6 -2
  48. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/serializer_json.py +9 -3
  49. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/db.json +8 -1
  50. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/field.json +18 -1
  51. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/super_resolve.py +4 -0
  52. metaobjects-0.15.0/tests/codegen/test_extends_abstract_field_inheritance.py +97 -0
  53. metaobjects-0.15.0/tests/codegen/test_reverse_finders.py +204 -0
  54. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_type_map.py +18 -0
  55. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_field_uuid_dbcolumntype.py +90 -1
  56. metaobjects-0.15.0/tests/unit/test_n2m_resolver_inherited.py +144 -0
  57. metaobjects-0.15.0/tests/unit/test_write_coercion_arrays.py +83 -0
  58. {metaobjects-0.14.0 → metaobjects-0.15.0}/uv.lock +1 -1
  59. {metaobjects-0.14.0 → metaobjects-0.15.0}/.gitignore +0 -0
  60. {metaobjects-0.14.0 → metaobjects-0.15.0}/LICENSE +0 -0
  61. {metaobjects-0.14.0 → metaobjects-0.15.0}/README.md +0 -0
  62. {metaobjects-0.14.0 → metaobjects-0.15.0}/hatch_build.py +0 -0
  63. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/__init__.py +0 -0
  64. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/agent_context/__init__.py +0 -0
  65. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/agent_context/scaffold.py +0 -0
  66. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/__init__.py +0 -0
  67. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/api_model.py +0 -0
  68. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/paths.py +0 -0
  69. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/apidocs/renderer.py +0 -0
  70. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/attr_class_map.py +0 -0
  71. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  72. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/__init__.py +0 -0
  73. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/config.py +0 -0
  74. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/constants.py +0 -0
  75. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  76. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/format.py +0 -0
  77. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generator.py +0 -0
  78. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generator_registry.py +0 -0
  79. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
  80. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  81. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  82. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  83. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  84. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  85. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  86. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  87. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  88. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/datatype.py +0 -0
  89. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/documentation/__init__.py +0 -0
  90. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/documentation/doc_constants.py +0 -0
  91. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/documentation/doc_provider.py +0 -0
  92. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/documentation/doc_schema.py +0 -0
  93. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/errors.py +0 -0
  94. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/__init__.py +0 -0
  95. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
  96. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/__init__.py +0 -0
  97. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
  98. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/file_source.py +0 -0
  99. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  100. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
  101. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/__init__.py +0 -0
  102. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/__init__.py +0 -0
  103. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  104. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  105. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  106. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/field/__init__.py +0 -0
  107. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  108. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  109. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  110. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/__init__.py +0 -0
  111. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  112. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  113. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  114. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
  115. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  116. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  117. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  118. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  119. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/meta_root.py +0 -0
  120. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/__init__.py +0 -0
  121. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  122. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  123. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  124. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  125. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  126. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  127. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/__init__.py +0 -0
  128. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  129. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  130. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  131. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  132. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  133. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  134. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  135. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  136. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/provider_extends.py +0 -0
  137. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/template/__init__.py +0 -0
  138. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  139. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/meta/template/template_constants.py +0 -0
  140. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/naming_refs.py +0 -0
  141. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/parser_yaml.py +0 -0
  142. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/provider.py +0 -0
  143. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/py.typed +0 -0
  144. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/registry.py +0 -0
  145. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/__init__.py +0 -0
  146. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/email_document.py +0 -0
  147. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/escapers.py +0 -0
  148. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  149. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/__init__.py +0 -0
  150. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/coerce.py +0 -0
  151. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/extract.py +0 -0
  152. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/extract_map.py +0 -0
  153. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  154. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/locate.py +0 -0
  155. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/normalize.py +0 -0
  156. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/strip.py +0 -0
  157. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/types.py +0 -0
  158. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  159. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/filesystem_provider.py +0 -0
  160. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/__init__.py +0 -0
  161. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  162. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  163. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  164. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  165. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  166. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/renderer.py +0 -0
  167. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/render/verify.py +0 -0
  168. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/runtime/__init__.py +0 -0
  169. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
  170. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/shared/__init__.py +0 -0
  171. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/shared/base_types.py +0 -0
  172. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/shared/separators.py +0 -0
  173. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/shared/structural.py +0 -0
  174. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/source/__init__.py +0 -0
  175. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/source/error_source.py +0 -0
  176. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/source/json_path.py +0 -0
  177. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/source/semantic_diff.py +0 -0
  178. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/source/yaml_positions.py +0 -0
  179. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  180. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/attr.json +0 -0
  181. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  182. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
  183. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
  184. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/object.json +0 -0
  185. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/origin.json +0 -0
  186. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  187. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  188. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/source.json +0 -0
  189. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/template.json +0 -0
  190. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
  191. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
  192. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/spec_metamodel/view.json +0 -0
  193. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/validation_types.py +0 -0
  194. {metaobjects-0.14.0 → metaobjects-0.15.0}/src/metaobjects/yaml_desugar.py +0 -0
  195. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/__init__.py +0 -0
  196. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/__init__.py +0 -0
  197. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  198. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/extends/expected/Program.py +0 -0
  199. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/extends/meta.json +0 -0
  200. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  201. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  202. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/nested-array/meta.json +0 -0
  203. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  204. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  205. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/scalars/meta.json +0 -0
  206. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  207. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/golden/vanilla/meta.json +0 -0
  208. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_abstract_conformance.py +0 -0
  209. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_api_docs_builder.py +0 -0
  210. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_api_docs_paths.py +0 -0
  211. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_cli.py +0 -0
  212. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_cli_registry.py +0 -0
  213. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  214. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_cli_verify_strict.py +0 -0
  215. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  216. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_constants_config.py +0 -0
  217. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_entity_model.py +0 -0
  218. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_enum_conformance.py +0 -0
  219. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_extractor_generator.py +0 -0
  220. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  221. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_format.py +0 -0
  222. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
  223. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  224. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_generator.py +0 -0
  225. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_generator_extension_seams.py +0 -0
  226. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_golden.py +0 -0
  227. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  228. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_inheritance_conformance.py +0 -0
  229. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_instance_artifacts.py +0 -0
  230. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_m2m_codegen.py +0 -0
  231. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_output_parser_generator.py +0 -0
  232. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_overwrite_policy.py +0 -0
  233. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_payload_vo_generator.py +0 -0
  234. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_projection_compile.py +0 -0
  235. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_render_helper_conformance.py +0 -0
  236. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_render_helper_generator.py +0 -0
  237. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_router_generator.py +0 -0
  238. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_runner.py +0 -0
  239. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_template_data.py +0 -0
  240. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_template_generator.py +0 -0
  241. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_template_output_pattern.py +0 -0
  242. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_template_scope_helpers.py +0 -0
  243. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_template_scope_walk.py +0 -0
  244. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_template_spec.py +0 -0
  245. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_tph_codegen.py +0 -0
  246. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_trace_helper_generator.py +0 -0
  247. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/codegen/test_validation_conformance.py +0 -0
  248. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/__init__.py +0 -0
  249. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/capabilities.py +0 -0
  250. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/conformance-expected-failures.json +0 -0
  251. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/conformance_adapter.py +0 -0
  252. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/corpus.py +0 -0
  253. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/expected_failures.py +0 -0
  254. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/fixture_discovery.py +0 -0
  255. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/navigator.py +0 -0
  256. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  257. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_conformance.py +0 -0
  258. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_extract_conformance.py +0 -0
  259. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_extract_object_verdict.py +0 -0
  260. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  261. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_fr011_attrs.py +0 -0
  262. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
  263. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_object_model_conformance.py +0 -0
  264. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_registry_conformance.py +0 -0
  265. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_runner_hardfail.py +0 -0
  266. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  267. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_strict_attr_load.py +0 -0
  268. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_template_codegen_conformance.py +0 -0
  269. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_template_generator_conformance.py +0 -0
  270. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/test_yaml_conformance.py +0 -0
  271. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  272. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/__init__.py +0 -0
  273. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/api_contract_assertions.py +0 -0
  274. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/api_contract_jsonb_server.py +0 -0
  275. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/api_contract_m2m_server.py +0 -0
  276. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/api_contract_server.py +0 -0
  277. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/generated_jsonb_app.py +0 -0
  278. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/generated_m2m_app.py +0 -0
  279. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/generated_router_app.py +0 -0
  280. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/generated_tph_app.py +0 -0
  281. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/meta_ai_trace.yaml +0 -0
  282. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/normalization.py +0 -0
  283. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/postgres_container.py +0 -0
  284. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/query_runner.py +0 -0
  285. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/scenarios.py +0 -0
  286. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_api_contract.py +0 -0
  287. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_generated.py +0 -0
  288. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_jsonb.py +0 -0
  289. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_m2m.py +0 -0
  290. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  291. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
  292. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_llm_call_trace.py +0 -0
  293. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_normalization.py +0 -0
  294. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_query_scenarios.py +0 -0
  295. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/integration/test_runtime_return_types.py +0 -0
  296. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/open_closed_proof_test.py +0 -0
  297. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/__init__.py +0 -0
  298. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/__init__.py +0 -0
  299. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_coerce.py +0 -0
  300. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_extract.py +0 -0
  301. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_extract_map.py +0 -0
  302. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  303. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_locate.py +0 -0
  304. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_model.py +0 -0
  305. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_normalize.py +0 -0
  306. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_strip.py +0 -0
  307. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  308. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/prompt/__init__.py +0 -0
  309. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
  310. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/test_email_document.py +0 -0
  311. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/test_filesystem_provider.py +0 -0
  312. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/test_output_format_renderer_nested.py +0 -0
  313. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/test_output_prompt_conformance.py +0 -0
  314. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/test_render_conformance.py +0 -0
  315. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/test_render_max_chars.py +0 -0
  316. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/test_verify.py +0 -0
  317. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/render/test_verify_conformance.py +0 -0
  318. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/source/__init__.py +0 -0
  319. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
  320. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
  321. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
  322. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/source/test_json_path.py +0 -0
  323. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/source/test_semantic_diff.py +0 -0
  324. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/source/test_source_on_node.py +0 -0
  325. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/source/test_yaml_positions.py +0 -0
  326. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/test_api_docs_accuracy.py +0 -0
  327. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/__init__.py +0 -0
  328. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_agent_context_staleness.py +0 -0
  329. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_capabilities.py +0 -0
  330. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_common_attrs.py +0 -0
  331. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_core_types.py +0 -0
  332. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_effective_package.py +0 -0
  333. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_errors.py +0 -0
  334. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_field_enum.py +0 -0
  335. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_field_map_validation.py +0 -0
  336. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  337. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_llm_recorder.py +0 -0
  338. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_loader.py +0 -0
  339. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_loader_bom.py +0 -0
  340. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_loader_class.py +0 -0
  341. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_merge.py +0 -0
  342. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_meta_attr.py +0 -0
  343. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_meta_data.py +0 -0
  344. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_meta_source.py +0 -0
  345. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_module_shortcuts.py +0 -0
  346. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_n2m_resolver.py +0 -0
  347. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  348. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_one_primary_source.py +0 -0
  349. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_parser.py +0 -0
  350. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_parser_yaml.py +0 -0
  351. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_provider.py +0 -0
  352. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_provider_extension.py +0 -0
  353. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_registry.py +0 -0
  354. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_registry_completeness.py +0 -0
  355. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_registry_extend.py +0 -0
  356. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_registry_sealed.py +0 -0
  357. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_relationship_referential_actions.py +0 -0
  358. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_resolution_key.py +0 -0
  359. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  360. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_serializer.py +0 -0
  361. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_shared_constants.py +0 -0
  362. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_smoke.py +0 -0
  363. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_sources.py +0 -0
  364. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_strict_child_placement.py +0 -0
  365. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_super_resolve.py +0 -0
  366. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_template_toolcall.py +0 -0
  367. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  368. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_validation_attr_schema.py +0 -0
  369. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_validation_filter_values.py +0 -0
  370. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_validation_origin_paths.py +0 -0
  371. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_validation_sort_field.py +0 -0
  372. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_validation_warnings.py +0 -0
  373. {metaobjects-0.14.0 → metaobjects-0.15.0}/tests/unit/test_yaml_desugar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaobjects
3
- Version: 0.14.0
3
+ Version: 0.15.0
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.14.0"
3
+ version = "0.15.0"
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"
@@ -85,21 +85,21 @@ def _is_writable_table_entity(obj: MetaObject, object_index: dict[str, MetaObjec
85
85
 
86
86
 
87
87
  def _template_format(tmpl: MetaData) -> str:
88
- fmt = tmpl.attr(tc.TEMPLATE_ATTR_FORMAT)
88
+ fmt = tmpl.get_meta_attr(tc.TEMPLATE_ATTR_FORMAT) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
89
89
  return fmt if isinstance(fmt, str) and fmt else tc.TEMPLATE_FORMAT_DEFAULT
90
90
 
91
91
 
92
92
  def _payload_resolves(tmpl: MetaData, root: MetaData) -> MetaObject | None:
93
93
  """The payload VO a template resolves to (``@payloadRef`` → ``object.value``),
94
94
  or ``None`` — the shared gate for payload / render / prompt / parser / extractor."""
95
- payload_ref = tmpl.attr(tc.TEMPLATE_ATTR_PAYLOAD_REF)
95
+ payload_ref = tmpl.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
96
96
  if not isinstance(payload_ref, str) or not payload_ref:
97
97
  return None
98
98
  return resolve_payload_vo(root, payload_ref)
99
99
 
100
100
 
101
101
  def _is_email_kind(tmpl: MetaData) -> bool:
102
- kind = tmpl.attr(tc.TEMPLATE_ATTR_KIND)
102
+ kind = tmpl.get_meta_attr(tc.TEMPLATE_ATTR_KIND) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
103
103
  return isinstance(kind, str) and kind.lower() == tc.TEMPLATE_KIND_EMAIL
104
104
 
105
105
 
@@ -113,6 +113,7 @@ class PythonApiModelBuilder:
113
113
 
114
114
  def build(self, root: MetaData, project: str) -> ApiModel:
115
115
  objects = [
116
+ # ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
116
117
  c
117
118
  for c in root.own_children()
118
119
  if c.type == TYPE_OBJECT and isinstance(c, MetaObject)
@@ -127,6 +128,7 @@ class PythonApiModelBuilder:
127
128
 
128
129
  # Templates: only template.output is consumed by the payload/render/prompt/
129
130
  # parser/extractor generators (the other template subtypes emit nothing).
131
+ # ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
130
132
  for tmpl in root.own_children():
131
133
  if tmpl.type == TYPE_TEMPLATE and tmpl.sub_type == tc.TEMPLATE_SUBTYPE_OUTPUT:
132
134
  units.append(self._build_template_unit(tmpl, root))
@@ -353,7 +355,7 @@ class PythonApiModelBuilder:
353
355
  values = type_map.effective_enum_values(f)
354
356
  if values:
355
357
  note = "allowed: " + " | ".join(values)
356
- required = f.attr(fc.FIELD_ATTR_REQUIRED) is True
358
+ required = f.get_meta_attr(fc.FIELD_ATTR_REQUIRED) is True # ADR-0039: resolving (@required may be inherited)
357
359
  rows.append(FieldShape(f.name, type_expr, optional=not required, note=note))
358
360
  return rows
359
361
 
@@ -39,6 +39,9 @@ __all__ = [
39
39
  "output_prompt_fn",
40
40
  "output_parser_fn",
41
41
  "extractor_fn",
42
+ "reverse_finder_fk_segment",
43
+ "reverse_finder_fn",
44
+ "reverse_finder_in_fn",
42
45
  ]
43
46
 
44
47
 
@@ -121,3 +124,75 @@ def output_parser_fn(template_name: str) -> str:
121
124
  def extractor_fn(template_name: str) -> str:
122
125
  """``OrderSummary`` → ``extract_order_summary`` (the strict extractor fn)."""
123
126
  return f"extract_{snake_case(template_name)}"
127
+
128
+
129
+ # ---------------------------------------------------------------------------
130
+ # ADR-0038 — reverse-relationship navigation as explicit FK finders.
131
+ #
132
+ # For each FK an entity ``E`` holds (an ``identity.reference`` over an FK field
133
+ # referencing entity ``T``), ``E``'s repository surface gains a finder returning
134
+ # the ``E`` rows matching a given ``T`` id — so ``T`` navigates to its referencing
135
+ # ``E`` rows by calling the finder with a ``T`` id. Two variants: a single-value
136
+ # finder and a batched (anti-N+1) ``…_in`` finder. Both are plain, framework-free
137
+ # single queries (``WHERE <fk> = ?`` / ``WHERE <fk> IN (…)``) — NOT lazy ORM
138
+ # collections (ADR-0038: lazy collections are impossible framework-free and are
139
+ # the canonical N+1 anti-pattern).
140
+ #
141
+ # CANONICAL NAMING (the cross-port contract — idiomatic Python snake_case spelling
142
+ # of the cross-port shape ``find<EPlural>By<FkField>`` / ``…In``):
143
+ #
144
+ # find_<e_plural>_by_<fk_segment>(value) → SELECT … FROM E WHERE <fk> = ?
145
+ # find_<e_plural>_by_<fk_segment>_in(values) → SELECT … FROM E WHERE <fk> IN (…)
146
+ #
147
+ # where:
148
+ # - <e_plural> is the source entity name pluralized then snake_cased
149
+ # (``GameSession`` → ``game_sessions``).
150
+ # - <fk_segment> is the FK FIELD name (NOT the relationship/navigation name and
151
+ # NOT the raw column), snake_cased, with a single trailing ``_id`` dropped if
152
+ # present. The FK field name is unique within an entity, so the finder name is
153
+ # unique by construction — this dissolves the same-pair collision and removes
154
+ # any need for a naming attribute.
155
+ #
156
+ # SAME-PAIR EXAMPLE (``GameSession`` has THREE FKs to ``Scene``):
157
+ # FK field ``currentSceneId`` → find_game_sessions_by_current_scene
158
+ # FK field ``lastOpeningNarrativeSceneId`` → find_game_sessions_by_last_opening_narrative_scene
159
+ # FK field ``transitioningFromSceneId`` → find_game_sessions_by_transitioning_from_scene
160
+ # Three distinct finders — no collision.
161
+ # ---------------------------------------------------------------------------
162
+
163
+
164
+ def _pluralize(name: str) -> str:
165
+ """Trivial cross-port pluralization (matches the TS ``pluralize`` /
166
+ ``MetaSource._pluralize``), applied to a PascalCase entity name BEFORE
167
+ snake-casing (``GameSession`` → ``GameSessions``)."""
168
+ if not name:
169
+ return name
170
+ lower = name.lower()
171
+ if lower.endswith(("s", "x", "z", "ch", "sh")):
172
+ return name + "es"
173
+ if len(name) >= 2 and lower[-1] == "y" and lower[-2] not in "aeiou":
174
+ return name[:-1] + "ies"
175
+ return name + "s"
176
+
177
+
178
+ def reverse_finder_fk_segment(fk_field_name: str) -> str:
179
+ """Lower an FK field name to the ``<fk_segment>`` of a reverse finder name:
180
+ snake_case the field, then drop a single trailing ``_id`` if present.
181
+ E.g. ``currentSceneId`` → ``current_scene``, ``authorId`` → ``author``,
182
+ ``scene`` → ``scene``."""
183
+ snake = snake_case(fk_field_name)
184
+ # Drop a single trailing "_id" (but not a bare "id" — that would yield "").
185
+ if len(snake) > 3 and snake.endswith("_id"):
186
+ return snake[:-3]
187
+ return snake
188
+
189
+
190
+ def reverse_finder_fn(source_entity_name: str, fk_field_name: str) -> str:
191
+ """Reverse single-value finder name: ``find_<e_plural>_by_<fk_segment>``."""
192
+ e_plural = snake_case(_pluralize(source_entity_name))
193
+ return f"find_{e_plural}_by_{reverse_finder_fk_segment(fk_field_name)}"
194
+
195
+
196
+ def reverse_finder_in_fn(source_entity_name: str, fk_field_name: str) -> str:
197
+ """Reverse batched finder name: ``find_<e_plural>_by_<fk_segment>_in``."""
198
+ return f"{reverse_finder_fn(source_entity_name, fk_field_name)}_in"
@@ -521,6 +521,7 @@ def _verify_templates(args: argparse.Namespace) -> int:
521
521
  # Toolcall templates have no renderable text body (the body IS the schema) —
522
522
  # skip them; there is no {{field}} drift to check.
523
523
  templates = [
524
+ # ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
524
525
  c
525
526
  for c in root.own_children()
526
527
  if c.type == TYPE_TEMPLATE and c.sub_type != tc.TEMPLATE_SUBTYPE_TOOLCALL
@@ -533,7 +534,7 @@ def _verify_templates(args: argparse.Namespace) -> int:
533
534
  error_count = 0
534
535
  checked = 0
535
536
  for tmpl in sorted(templates, key=lambda c: c.name):
536
- payload_ref = tmpl.attr(tc.TEMPLATE_ATTR_PAYLOAD_REF)
537
+ payload_ref = tmpl.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
537
538
  if not isinstance(payload_ref, str) or not payload_ref:
538
539
  print(f"error: [{tmpl.name}] missing @payloadRef.", file=sys.stderr)
539
540
  error_count += 1
@@ -553,7 +554,8 @@ def _verify_templates(args: argparse.Namespace) -> int:
553
554
  refs = [
554
555
  val
555
556
  for a in _TEMPLATE_TEXT_REF_ATTRS
556
- if isinstance(val := tmpl.attr(a), str) and val
557
+ # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
558
+ if isinstance(val := tmpl.get_meta_attr(a), str) and val
557
559
  ]
558
560
  if not refs:
559
561
  print(
@@ -36,7 +36,12 @@ from metaobjects.shared.separators import PACKAGE_SEP
36
36
 
37
37
 
38
38
  def _find_object(root: MetaData, name: str) -> MetaData | None:
39
- """The own-child ``object.*`` node named *name*, or ``None``."""
39
+ """The top-level ``object.*`` node named *name*, or ``None``.
40
+
41
+ ADR-0039 sanctioned own: top-level object lookup on the loader ROOT
42
+ (metadata.root is never extended, so own == effective) — mirrors the TS
43
+ reference (``root.ownChildren()``).
44
+ """
40
45
  for c in root.own_children():
41
46
  if c.type == TYPE_OBJECT and c.name == name:
42
47
  return c
@@ -22,7 +22,6 @@ from metaobjects.meta.core.field import field_constants as fc
22
22
  from metaobjects.meta.meta_data import MetaData
23
23
  from metaobjects.meta.template.template_constants import TEMPLATE_ATTR_XML_TEXT
24
24
  from metaobjects.shared.base_types import TYPE_FIELD
25
- from metaobjects.shared.structural import KEY_IS_ARRAY
26
25
 
27
26
 
28
27
  def fields(vo: MetaData) -> list[MetaData]:
@@ -32,9 +31,10 @@ def fields(vo: MetaData) -> list[MetaData]:
32
31
 
33
32
 
34
33
  def is_array(field: MetaData) -> bool:
35
- """Array-ness from either form: the node property (programmatic build) or the
36
- ``@isArray`` attr (how metadata loads from JSON)."""
37
- return bool(field.is_array) or field.attrs().get(KEY_IS_ARRAY) is True
34
+ """Effective array-ness (ADR-0039 resolving): the native ``is_array`` flag OR the
35
+ ``@isArray`` attr, resolved through the ``extends`` super chain so a concrete field
36
+ inheriting array-ness from an abstract parent is honored."""
37
+ return field.resolved_is_array()
38
38
 
39
39
 
40
40
  def is_required(field: MetaData) -> bool:
@@ -63,31 +63,36 @@ def enum_values(field: MetaData) -> list[str]:
63
63
  return []
64
64
 
65
65
 
66
- def _own_attr_string(node: MetaData, name: str) -> str | None:
67
- """The own (locally declared) string value of attr *name*, or ``None``."""
68
- v = node.attr(name)
66
+ def _attr_string(node: MetaData, name: str) -> str | None:
67
+ """The RESOLVING string value of attr *name* (own + inherited via ``extends``),
68
+ or ``None``.
69
+
70
+ ADR-0039 — effective read: a concrete enum field extending an abstract enum may
71
+ inherit ``@coerceDefault``/``@default``/``@normalize`` from the abstract parent,
72
+ so these resolve through the super chain (``get_meta_attr``), never own-only."""
73
+ v = node.get_meta_attr(name)
69
74
  return v if isinstance(v, str) else None
70
75
 
71
76
 
72
77
  def coerce_default(field: MetaData) -> str | None:
73
- """FR-011: the enum field's own ``@coerceDefault`` member, or ``None``."""
74
- return _own_attr_string(field, fc.FIELD_ATTR_COERCE_DEFAULT)
78
+ """FR-011: the enum field's effective ``@coerceDefault`` member, or ``None``."""
79
+ return _attr_string(field, fc.FIELD_ATTR_COERCE_DEFAULT)
75
80
 
76
81
 
77
82
  def default_value(field: MetaData) -> str | None:
78
- """FR-011: the enum field's own ``@default`` absent-fill member, or ``None``."""
79
- return _own_attr_string(field, fc.FIELD_ATTR_DEFAULT)
83
+ """FR-011: the enum field's effective ``@default`` absent-fill member, or ``None``."""
84
+ return _attr_string(field, fc.FIELD_ATTR_DEFAULT)
80
85
 
81
86
 
82
87
  def resolve_normalize(field: MetaData, owner: MetaData | None) -> str:
83
88
  """FR-011: resolve the enum normalization mode — field-level ``@normalize``, else the
84
89
  owning ``object.value``'s ``@normalize`` (the per-object default), else the global
85
90
  default (``"strip"``). Mirrors the Java/Kotlin/C#/TS ``resolveNormalize``."""
86
- field_mode = _own_attr_string(field, fc.FIELD_ATTR_NORMALIZE)
91
+ field_mode = _attr_string(field, fc.FIELD_ATTR_NORMALIZE)
87
92
  if field_mode is not None:
88
93
  return field_mode
89
94
  if owner is not None:
90
- owner_mode = _own_attr_string(owner, fc.FIELD_ATTR_NORMALIZE)
95
+ owner_mode = _attr_string(owner, fc.FIELD_ATTR_NORMALIZE)
91
96
  if owner_mode is not None:
92
97
  return owner_mode
93
98
  return fc.NORMALIZE_DEFAULT
@@ -101,6 +106,9 @@ def scalar_kind(sub_type: str) -> str | None:
101
106
  fc.FIELD_SUBTYPE_DATE,
102
107
  fc.FIELD_SUBTYPE_TIME,
103
108
  fc.FIELD_SUBTYPE_TIMESTAMP,
109
+ # ADR-0036/0037 Wave 3: uri/inet are string scalars on the wire.
110
+ fc.FIELD_SUBTYPE_URI,
111
+ fc.FIELD_SUBTYPE_INET,
104
112
  ):
105
113
  return "STRING"
106
114
  if sub_type == fc.FIELD_SUBTYPE_INT:
@@ -48,6 +48,18 @@ def _is_sql_expr_default(value: object) -> bool:
48
48
  return isinstance(value, str) and any(p.search(value) for p in _SQL_EXPR_DEFAULT_PATTERNS)
49
49
 
50
50
 
51
+ # ADR-0036/0037 Wave 3 — the CANONICAL hostname matcher for @stringFormat: hostname.
52
+ # The matcher lives in codegen (NOT author validator.regex) so every port replicates
53
+ # the SAME canonical form — cross-language regex engines diverge, so this one
54
+ # expression is the byte-identical source of truth. RFC 1123 labels: 1–63 chars each,
55
+ # alphanumeric + internal hyphens, dot-separated; total length 1–253; anchored.
56
+ # Byte-identical to the TS zod-validators HOSTNAME_REGEX_LITERAL body.
57
+ _HOSTNAME_REGEX = (
58
+ r"^(?=.{1,253}$)([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)"
59
+ r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
60
+ )
61
+
62
+
51
63
  def _validators(field: MetaField, sub_type: str) -> list[MetaField]:
52
64
  """The field's own ``validator.<sub_type>`` children (effective, supers included)."""
53
65
  return [
@@ -60,6 +72,10 @@ def _validators(field: MetaField, sub_type: str) -> list[MetaField]:
60
72
  def _first_attr(field: MetaField, sub_type: str, attr_name: str) -> object | None:
61
73
  """First int-valued *attr_name* across the field's ``validator.<sub_type>`` children."""
62
74
  for v in _validators(field, sub_type):
75
+ # ADR-0039 sanctioned own: a validator's @min/@max are its own declared
76
+ # bounds (never inherited across a validator extends chain) — matches the TS
77
+ # MetaValidator.min/.max ownAttr reads. The validator nodes are located via
78
+ # the resolving field.children() in _validators().
63
79
  val = v.attr(attr_name)
64
80
  if _is_int(val):
65
81
  return val
@@ -106,11 +122,21 @@ def _validator_constraints(field: MetaField) -> dict[str, object]:
106
122
 
107
123
  # Regex: validator.regex @pattern -> pattern.
108
124
  for v in _validators(field, vc.VALIDATOR_SUBTYPE_REGEX):
125
+ # ADR-0039 sanctioned own: a validator's @pattern is its own declared value
126
+ # (matches the TS MetaValidator.pattern ownAttr read); the validator node is
127
+ # located via the resolving field.children() in _validators().
109
128
  pattern = v.attr(vc.VALIDATOR_ATTR_PATTERN)
110
129
  if isinstance(pattern, str):
111
130
  kwargs["pattern"] = pattern
112
131
  break
113
132
 
133
+ # ADR-0036/0037 Wave 3 — @stringFormat: hostname -> a codegen-owned canonical
134
+ # hostname pattern= (the matcher lives HERE, never author validator.regex, so
135
+ # every port replicates the byte-identical form). @stringFormat: email is bound
136
+ # as EmailStr in _field_line (a type, not a pattern), so it is not handled here.
137
+ if field.attrs().get(fc.FIELD_ATTR_STRING_FORMAT) == fc.STRING_FORMAT_HOSTNAME:
138
+ kwargs["pattern"] = _HOSTNAME_REGEX
139
+
114
140
  return kwargs
115
141
 
116
142
 
@@ -132,6 +158,17 @@ def _field_line(field: MetaField, imports: set[str], config: GenConfig) -> tuple
132
158
  pt = py_type_for(field)
133
159
  imports.update(pt.imports)
134
160
  type_expr = pt.expr
161
+ # ADR-0036/0037 Wave 3 — @stringFormat: email narrows a plain string to a
162
+ # validated email. Bind Pydantic's idiomatic EmailStr (the Python analogue of
163
+ # Zod .email()); hostname stays a plain str with a codegen-owned canonical
164
+ # pattern= (handled in _validator_constraints). The field stays a string.
165
+ if (
166
+ field.sub_type == fc.FIELD_SUBTYPE_STRING
167
+ and field.attrs().get(fc.FIELD_ATTR_STRING_FORMAT) == fc.STRING_FORMAT_EMAIL
168
+ ):
169
+ base_expr = "EmailStr"
170
+ imports.add("from pydantic import EmailStr")
171
+ type_expr = f"list[{base_expr}]" if field_is_array(field) else base_expr
135
172
  if field.sub_type in (fc.FIELD_SUBTYPE_OBJECT, fc.FIELD_SUBTYPE_MAP):
136
173
  # A field.object (-> VO) and a field.map with @objectRef (-> dict[str, VO])
137
174
  # both reference a value-object by name; import it so the annotation
@@ -238,6 +275,9 @@ class EntityModelGenerator:
238
275
  cfg = config if config is not None else GenConfig(out_dir="")
239
276
  uses_field = False
240
277
  lines: list[str] = []
278
+ # ADR-0039 SANCTIONED OWN — codegen subclass-emit: the generated model
279
+ # `extends` its generated base, so only OWN fields are emitted here (the base
280
+ # class already declares inherited ones). Pinned by test_entity_model.py.
241
281
  for f in entity.own_fields():
242
282
  line, used = _field_line(f, imports, cfg)
243
283
  uses_field = uses_field or used
@@ -151,14 +151,14 @@ def render_extractor(
151
151
  Returns ``None`` when the ``@payloadRef`` can't be resolved to an ``object.value``,
152
152
  or when the target ``@format`` is not json/xml (the extract tier requires the
153
153
  tolerant extract API, which only the json/xml output-parsers emit)."""
154
- payload_ref = template.attr(tc.TEMPLATE_ATTR_PAYLOAD_REF)
154
+ payload_ref = template.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
155
155
  if not isinstance(payload_ref, str) or not payload_ref:
156
156
  return None
157
157
  payload = resolve_payload_vo(root, payload_ref)
158
158
  if payload is None:
159
159
  return None
160
160
 
161
- fmt = template.attr(tc.TEMPLATE_ATTR_FORMAT)
161
+ fmt = template.get_meta_attr(tc.TEMPLATE_ATTR_FORMAT) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
162
162
  fmt_str = fmt if isinstance(fmt, str) else tc.TEMPLATE_FORMAT_DEFAULT
163
163
  if fmt_str.lower() not in _EXTRACT_FORMATS:
164
164
  return None
@@ -278,6 +278,7 @@ class ExtractorGenerator:
278
278
  files: list[EmittedFile] = []
279
279
  outputs = sorted(
280
280
  (
281
+ # ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
281
282
  c
282
283
  for c in root.own_children()
283
284
  if c.type == TYPE_TEMPLATE and c.sub_type == tc.TEMPLATE_SUBTYPE_OUTPUT
@@ -64,8 +64,12 @@ def _effective_fqn(entity: MetaObject) -> str:
64
64
 
65
65
 
66
66
  def _primary_source_rdb(entity: MetaObject) -> MetaSource | None:
67
- """Return the entity's ``source.rdb`` child (own only), or ``None``."""
68
- for c in entity.own_children():
67
+ """Return the entity's ``source.rdb`` child, or ``None``.
68
+
69
+ ADR-0039 — RESOLVING (children()): an entity may inherit its source.rdb via
70
+ ``extends`` (BaseEntity); own_children() would miss it. Mirrors TS dbTable.
71
+ """
72
+ for c in entity.children():
69
73
  if c.type == TYPE_SOURCE and isinstance(c, MetaSource):
70
74
  return c
71
75
  return None
@@ -91,7 +95,11 @@ def ops_for_subtype_ordered(sub_type: str | None) -> tuple[str, ...]:
91
95
  return ()
92
96
  if sub_type in (fc.FIELD_SUBTYPE_STRING, fc.FIELD_SUBTYPE_ENUM):
93
97
  return _OPS_STRING
94
- if sub_type == fc.FIELD_SUBTYPE_UUID:
98
+ # ADR-0036/0037 Wave 3 — field.uri is string-like (substring searchable);
99
+ # field.inet is uuid-like (identity value, no substring search, no ordering).
100
+ if sub_type == fc.FIELD_SUBTYPE_URI:
101
+ return _OPS_STRING
102
+ if sub_type in (fc.FIELD_SUBTYPE_UUID, fc.FIELD_SUBTYPE_INET):
95
103
  return _OPS_UUID
96
104
  if sub_type in (
97
105
  fc.FIELD_SUBTYPE_INT,
@@ -190,6 +198,10 @@ class FilterAllowlistGenerator:
190
198
  plan = tph_plan_for(entity, object_index)
191
199
  if plan is not None:
192
200
  for st in plan.subtypes:
201
+ # ADR-0039 sanctioned own: TPH subtype delta — the base's
202
+ # effective fields are added above via entity.fields(); each
203
+ # subtype contributes ONLY its own additional fields (inherited
204
+ # base fields must not be re-added).
193
205
  for f in st.entity.own_fields():
194
206
  add(f)
195
207
  return out
@@ -66,8 +66,12 @@ def resolve_shared_enum_decl(field: MetaField) -> MetaData | None:
66
66
 
67
67
 
68
68
  def is_provided(decl: MetaData) -> bool:
69
- """Own ``@provided`` truth of an enum declaration."""
70
- return decl.attr(fc.FIELD_ATTR_PROVIDED) is True
69
+ """Effective ``@provided`` truth of an enum declaration.
70
+
71
+ ADR-0039 — resolves through ``extends`` (``get_meta_attr``): a concrete enum
72
+ extending an abstract ``@provided`` enum inherits the flag, so an own-only read
73
+ would misclassify it."""
74
+ return decl.get_meta_attr(fc.FIELD_ATTR_PROVIDED) is True
71
75
 
72
76
 
73
77
  def _meta_package_of(decl: MetaData) -> str:
@@ -99,7 +103,9 @@ def collect_shared_enums(entities: list) -> list[SharedEnum]:
99
103
  not materialized (no dangling type)."""
100
104
  out: dict[str, SharedEnum] = {}
101
105
  for entity in entities:
102
- for f in entity.own_fields():
106
+ # ADR-0039 RESOLVING (fields()): mirrors the TS collectSharedEnums
107
+ # (``entity.fields()``); an inherited field consuming a shared enum counts.
108
+ for f in entity.fields():
103
109
  shared = shared_enum_for_field(f)
104
110
  if shared is None:
105
111
  continue
@@ -72,7 +72,10 @@ def plural_lowercase(name: str) -> str:
72
72
 
73
73
 
74
74
  def _primary_source_rdb(entity: MetaObject) -> MetaSource | None:
75
- for c in entity.own_children():
75
+ # ADR-0039 RESOLVING (children()): an entity may inherit its source.rdb from
76
+ # an abstract base via extends (BaseEntity pattern); own_children() would emit
77
+ # nothing for such an entity. Mirrors the TS dbTable source lookup.
78
+ for c in entity.children():
76
79
  if c.type == TYPE_SOURCE and isinstance(c, MetaSource):
77
80
  return c
78
81
  return None
@@ -111,7 +114,7 @@ def _pk_field_name(entity: MetaObject) -> str:
111
114
  """The single PK field name (``identity.primary @fields``), default ``id``."""
112
115
  for c in entity.children():
113
116
  if c.type == TYPE_IDENTITY and c.sub_type == IDENTITY_SUBTYPE_PRIMARY:
114
- fields = c.attr(IDENTITY_ATTR_FIELDS)
117
+ fields = c.get_meta_attr(IDENTITY_ATTR_FIELDS) # ADR-0039: resolving (identity attr)
115
118
  if isinstance(fields, (list, tuple)) and fields:
116
119
  first = fields[0]
117
120
  if isinstance(first, str):
@@ -122,10 +125,14 @@ def _pk_field_name(entity: MetaObject) -> str:
122
125
 
123
126
 
124
127
  def m2m_relationships(entity: MetaObject) -> list[MetaRelationship]:
125
- """The entity's own ``relationship.* @cardinality:"many" + @through`` children
126
- (declaration order). 1:N relationships (no ``@through``) are excluded."""
128
+ """The entity's ``relationship.* @cardinality:"many" + @through`` children
129
+ (declaration order). 1:N relationships (no ``@through``) are excluded.
130
+
131
+ ADR-0039 — RESOLVING (children()): mirrors the TS ``relationships()`` (built on
132
+ the effective ``children()``); a relationship inherited via ``extends`` counts.
133
+ """
127
134
  out: list[MetaRelationship] = []
128
- for c in entity.own_children():
135
+ for c in entity.children():
129
136
  if not isinstance(c, MetaRelationship):
130
137
  continue
131
138
  if c.cardinality() != CARDINALITY_MANY:
@@ -54,7 +54,7 @@ def render_output_parser(template: MetaData, root: MetaData) -> str | None:
54
54
 
55
55
  Returns ``None`` when the ``@payloadRef`` can't be resolved (defensive;
56
56
  the loader's template-validation pass would normally catch this first)."""
57
- payload_ref = template.attr(tc.TEMPLATE_ATTR_PAYLOAD_REF)
57
+ payload_ref = template.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
58
58
  if not isinstance(payload_ref, str) or not payload_ref:
59
59
  return None
60
60
  payload = resolve_payload_vo(root, payload_ref)
@@ -77,7 +77,7 @@ def render_output_parser(template: MetaData, root: MetaData) -> str | None:
77
77
  # template targets json/xml. Otherwise only the FR-006 strict parser is emitted
78
78
  # (text-format outputs get no extract). The mirror is a nullable twin of the
79
79
  # payload, so the strict ``parse_*`` is left exactly as FR-006 shipped it.
80
- fmt = template.attr(tc.TEMPLATE_ATTR_FORMAT)
80
+ fmt = template.get_meta_attr(tc.TEMPLATE_ATTR_FORMAT) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
81
81
  fmt_str = fmt if isinstance(fmt, str) else tc.TEMPLATE_FORMAT_DEFAULT
82
82
  emit_extract_lenient = fmt_str.lower() in _EXTRACT_FORMATS
83
83
  extracted_class = f"{payload_class}Extracted"
@@ -172,6 +172,8 @@ def render_output_parser(template: MetaData, root: MetaData) -> str | None:
172
172
  lines.append(" :param root: a loaded ``MetaRoot`` that declares the")
173
173
  lines.append(f' ``{payload.name}`` value-object."""')
174
174
  lines.append(" mo = None")
175
+ # Emits a root-scan into generated code: root is the loader ROOT (never
176
+ # extended, so own == effective) — ADR-0039 sanctioned own in emitted code.
175
177
  lines.append(" for child in root.own_children():")
176
178
  lines.append(" if (")
177
179
  lines.append(" isinstance(child, MetaObject)")
@@ -231,6 +233,7 @@ class OutputParserGenerator:
231
233
  files: list[EmittedFile] = []
232
234
  outputs = sorted(
233
235
  (
236
+ # ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
234
237
  c
235
238
  for c in root.own_children()
236
239
  if c.type == TYPE_TEMPLATE and c.sub_type == tc.TEMPLATE_SUBTYPE_OUTPUT
@@ -60,12 +60,12 @@ def render_output_prompt(
60
60
  Returns ``None`` when the format is unsupported (not json/xml) or the
61
61
  ``@payloadRef`` can't be resolved to an ``object.value`` (defensive — the loader
62
62
  validation pass / the parser generator share this contract)."""
63
- fmt = template.attr(tc.TEMPLATE_ATTR_FORMAT)
63
+ fmt = template.get_meta_attr(tc.TEMPLATE_ATTR_FORMAT) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
64
64
  fmt_str = fmt if isinstance(fmt, str) else tc.TEMPLATE_FORMAT_DEFAULT
65
65
  if fmt_str.lower() not in _PROMPT_FORMATS:
66
66
  return None
67
67
 
68
- payload_ref = template.attr(tc.TEMPLATE_ATTR_PAYLOAD_REF)
68
+ payload_ref = template.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
69
69
  if not isinstance(payload_ref, str) or not payload_ref:
70
70
  return None
71
71
  payload = resolve_payload_vo(root, payload_ref)
@@ -153,6 +153,7 @@ class OutputPromptGenerator:
153
153
  files: list[EmittedFile] = []
154
154
  outputs = sorted(
155
155
  (
156
+ # ADR-0039 sanctioned own: top-level scan on the loader ROOT (never extended, own == effective)
156
157
  c
157
158
  for c in root.own_children()
158
159
  if c.type == TYPE_TEMPLATE and c.sub_type == tc.TEMPLATE_SUBTYPE_OUTPUT
@@ -113,6 +113,9 @@ def _resolve_object_by_short_or_fqn(root: MetaData, ref: str) -> MetaObject | No
113
113
  bare ``fqn()`` / ``name`` (the latter covering legacy same-tree refs and
114
114
  root-level/empty-package objects). Mirrors TS ``refMatchesObject`` /
115
115
  ``KotlinGenUtil.resolveObjectByShortOrFqn``."""
116
+ # ADR-0039 sanctioned own: top-level object lookup on the loader ROOT
117
+ # (metadata.root is never extended, so own == effective) — mirrors the TS
118
+ # reference (``root.ownChildren()`` in naming-refs / validation-passes).
116
119
  for child in root.own_children():
117
120
  if child.type != TYPE_OBJECT or not isinstance(child, MetaObject):
118
121
  continue
@@ -256,7 +259,10 @@ def _enum_field_type(
256
259
 
257
260
  def _find_origin_child(field: MetaField) -> MetaOrigin | None:
258
261
  """First ``origin.*`` child of *field* (own children only — origins are
259
- declared inline; there's no inheritance contract for them today)."""
262
+ declared inline; there's no inheritance contract for them today).
263
+
264
+ ADR-0039 sanctioned own: origin.* NEVER inherits via extends (ADR-0029), so a
265
+ field's origin is read from its OWN children."""
260
266
  for c in field.own_children():
261
267
  if isinstance(c, MetaOrigin):
262
268
  return c
@@ -283,7 +289,7 @@ def _resolve_passthrough_type(
283
289
  """``origin.passthrough @from "Entity.field"`` — resolve to source field's
284
290
  Python type. Falls back to the payload field's own type when the dotted
285
291
  ref can't be resolved (defensive — loader validation already gates ``@from``)."""
286
- from_ref = origin.attr(ORIGIN_ATTR_FROM)
292
+ from_ref = origin.attr(ORIGIN_ATTR_FROM) # ADR-0039 sanctioned own: origin.* never inherits (ADR-0029)
287
293
  if not isinstance(from_ref, str) or not from_ref:
288
294
  return _fallback_type(fallback)
289
295
  source = _resolve_dotted_field_ref(root, from_ref)
@@ -301,13 +307,13 @@ def _resolve_aggregate_type(
301
307
  - avg → ``float``
302
308
  - sum / min / max → type of the ``@of`` field
303
309
  """
304
- agg = origin.attr(ORIGIN_ATTR_AGG)
310
+ agg = origin.attr(ORIGIN_ATTR_AGG) # ADR-0039 sanctioned own: origin.* never inherits (ADR-0029)
305
311
  if agg == "count":
306
312
  return "int", set()
307
313
  if agg == "avg":
308
314
  return "float", set()
309
315
  if agg in ("sum", "min", "max"):
310
- of_ref = origin.attr(ORIGIN_ATTR_OF)
316
+ of_ref = origin.attr(ORIGIN_ATTR_OF) # ADR-0039 sanctioned own: origin.* never inherits (ADR-0029)
311
317
  if not isinstance(of_ref, str) or not of_ref:
312
318
  return _fallback_type(fallback)
313
319
  source = _resolve_dotted_field_ref(root, of_ref)
@@ -333,7 +339,7 @@ def _resolve_collection_type(
333
339
  referenced by two fields in the same payload module, only one nested
334
340
  class is emitted. Cross-file dedupe would leave forward-references
335
341
  dangling (see the module docstring)."""
336
- via = origin.attr(ORIGIN_ATTR_VIA)
342
+ via = origin.attr(ORIGIN_ATTR_VIA) # ADR-0039 sanctioned own: origin.* never inherits (ADR-0029)
337
343
  if not isinstance(via, str) or not via:
338
344
  return _fallback_type(fallback)
339
345
  parts = _split_dotted_ref(via)
@@ -486,7 +492,7 @@ def render_payload_vo(
486
492
  on the same relationship), only one nested class is emitted. Across
487
493
  different templates, each file owns its full class graph independently —
488
494
  see the module docstring for the rationale."""
489
- payload_ref = template.attr(tc.TEMPLATE_ATTR_PAYLOAD_REF)
495
+ payload_ref = template.get_meta_attr(tc.TEMPLATE_ATTR_PAYLOAD_REF) # ADR-0039: template attr resolves via extends (not origin; templates CAN extend)
490
496
  if not isinstance(payload_ref, str) or not payload_ref:
491
497
  return None
492
498
  payload = resolve_payload_vo(root, payload_ref)
@@ -660,6 +666,8 @@ class PayloadVoGenerator:
660
666
  if root is None:
661
667
  return []
662
668
  files: list[EmittedFile] = []
669
+ # ADR-0039 sanctioned own: top-level template scan on the loader ROOT
670
+ # (metadata.root is never extended, so own == effective).
663
671
  templates = sorted(
664
672
  (c for c in root.own_children() if c.type == TYPE_TEMPLATE and isinstance(c, MetaTemplate)),
665
673
  key=lambda c: c.name,