metaobjects 0.23.1__tar.gz → 0.23.2__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 (410) hide show
  1. {metaobjects-0.23.1 → metaobjects-0.23.2}/PKG-INFO +1 -1
  2. {metaobjects-0.23.1 → metaobjects-0.23.2}/pyproject.toml +1 -1
  3. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +39 -4
  4. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/core_types.py +13 -0
  5. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/errors.py +6 -0
  6. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/validation_passes.py +215 -14
  7. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/attr/attr_constants.py +8 -0
  8. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/attr/meta_attr.py +17 -0
  9. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/field/field_constants.py +7 -0
  10. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/runtime/object_manager.py +94 -6
  11. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/attr.json +6 -0
  12. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/field.json +2 -1
  13. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/capabilities.py +7 -3
  14. metaobjects-0.23.2/tests/runtime/test_object_manager_enum_intvaluemap.py +192 -0
  15. metaobjects-0.23.2/tests/unit/test_field_enum_intvaluemap.py +185 -0
  16. {metaobjects-0.23.1 → metaobjects-0.23.2}/uv.lock +1 -1
  17. {metaobjects-0.23.1 → metaobjects-0.23.2}/.gitignore +0 -0
  18. {metaobjects-0.23.1 → metaobjects-0.23.2}/LICENSE +0 -0
  19. {metaobjects-0.23.1 → metaobjects-0.23.2}/README.md +0 -0
  20. {metaobjects-0.23.1 → metaobjects-0.23.2}/hatch_build.py +0 -0
  21. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/__init__.py +0 -0
  22. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/agent_context/__init__.py +0 -0
  23. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/agent_context/scaffold.py +0 -0
  24. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/apidocs/__init__.py +0 -0
  25. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/apidocs/api_model.py +0 -0
  26. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/apidocs/builder.py +0 -0
  27. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/apidocs/naming.py +0 -0
  28. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/apidocs/paths.py +0 -0
  29. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/apidocs/renderer.py +0 -0
  30. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/attr_class_map.py +0 -0
  31. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/cli.py +0 -0
  32. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  33. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/__init__.py +0 -0
  34. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/collision_names.py +0 -0
  35. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/config.py +0 -0
  36. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/constants.py +0 -0
  37. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  38. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  39. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/format.py +0 -0
  40. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  41. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generator.py +0 -0
  42. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generator_registry.py +0 -0
  43. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/__init__.py +0 -0
  44. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  45. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  46. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  47. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  48. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  49. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  50. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
  51. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  52. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/router_generator.py +0 -0
  53. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  54. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  55. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  56. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  57. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/metaobjects-config.schema.json +0 -0
  58. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  59. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  60. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/project_config.py +0 -0
  61. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/runner.py +0 -0
  62. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  63. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  64. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  65. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  66. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  67. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  68. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/codegen/type_map.py +0 -0
  69. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/datatype.py +0 -0
  70. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/documentation/__init__.py +0 -0
  71. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/documentation/doc_constants.py +0 -0
  72. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/documentation/doc_provider.py +0 -0
  73. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/documentation/doc_schema.py +0 -0
  74. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/__init__.py +0 -0
  75. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/merge.py +0 -0
  76. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/meta_data_loader.py +0 -0
  77. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/registered_validation.py +0 -0
  78. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/sources/__init__.py +0 -0
  79. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/sources/directory_source.py +0 -0
  80. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/sources/file_source.py +0 -0
  81. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  82. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/sources/uri_source.py +0 -0
  83. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/validate_discriminator.py +0 -0
  84. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
  85. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/validate_field_readonly.py +0 -0
  86. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  87. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  88. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  89. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/__init__.py +0 -0
  90. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/__init__.py +0 -0
  91. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  92. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/field/__init__.py +0 -0
  93. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  94. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  95. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  96. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  97. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/index/__init__.py +0 -0
  98. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  99. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  100. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/object/__init__.py +0 -0
  101. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  102. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  103. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  104. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  105. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  106. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/object/value_object.py +0 -0
  107. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  108. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  109. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  110. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  111. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
  112. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/requirement/meta_requirement.py +0 -0
  113. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/requirement/requirement_constants.py +0 -0
  114. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  115. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  116. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/meta_data.py +0 -0
  117. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/meta_root.py +0 -0
  118. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/__init__.py +0 -0
  119. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  120. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  121. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  122. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  123. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  124. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  125. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  126. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  127. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  128. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/__init__.py +0 -0
  129. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  130. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  131. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  132. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  133. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  134. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  135. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  136. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  137. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/provider_extends.py +0 -0
  138. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/template/__init__.py +0 -0
  139. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/template/meta_template.py +0 -0
  140. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  141. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/meta/template/template_constants.py +0 -0
  142. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/naming_refs.py +0 -0
  143. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/parser.py +0 -0
  144. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/parser_yaml.py +0 -0
  145. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/provider.py +0 -0
  146. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/py.typed +0 -0
  147. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/registry.py +0 -0
  148. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/registry_manifest.py +0 -0
  149. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/__init__.py +0 -0
  150. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/email_document.py +0 -0
  151. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/escapers.py +0 -0
  152. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  153. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/__init__.py +0 -0
  154. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/coerce.py +0 -0
  155. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/extract.py +0 -0
  156. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/extract_map.py +0 -0
  157. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  158. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/locate.py +0 -0
  159. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/normalize.py +0 -0
  160. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/strip.py +0 -0
  161. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/types.py +0 -0
  162. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  163. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/filesystem_provider.py +0 -0
  164. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/payload_accessors.py +0 -0
  165. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/prompt/__init__.py +0 -0
  166. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  167. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  168. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  169. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  170. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  171. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/renderer.py +0 -0
  172. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/render/verify.py +0 -0
  173. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/runtime/__init__.py +0 -0
  174. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/runtime/llm_recorder.py +0 -0
  175. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  176. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/runtime/tph.py +0 -0
  177. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/serializer_json.py +0 -0
  178. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/shared/__init__.py +0 -0
  179. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/shared/base_types.py +0 -0
  180. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/shared/separators.py +0 -0
  181. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/shared/structural.py +0 -0
  182. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/source/__init__.py +0 -0
  183. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/source/error_source.py +0 -0
  184. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/source/json_path.py +0 -0
  185. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/source/semantic_diff.py +0 -0
  186. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/source/yaml_positions.py +0 -0
  187. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  188. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/db.json +0 -0
  189. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  190. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/identity.json +0 -0
  191. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/index.json +0 -0
  192. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/layout.json +0 -0
  193. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/object.json +0 -0
  194. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/origin.json +0 -0
  195. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  196. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  197. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/requirement.json +0 -0
  198. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/source.json +0 -0
  199. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/template.json +0 -0
  200. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  201. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/ui.json +0 -0
  202. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/validator.json +0 -0
  203. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/spec_metamodel/view.json +0 -0
  204. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/super_resolve.py +0 -0
  205. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/validation_types.py +0 -0
  206. {metaobjects-0.23.1 → metaobjects-0.23.2}/src/metaobjects/yaml_desugar.py +0 -0
  207. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/__init__.py +0 -0
  208. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/__init__.py +0 -0
  209. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  210. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/extends/expected/Program.py +0 -0
  211. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/extends/meta.json +0 -0
  212. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  213. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  214. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/nested-array/meta.json +0 -0
  215. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  216. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  217. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/scalars/meta.json +0 -0
  218. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  219. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/golden/vanilla/meta.json +0 -0
  220. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_abstract_conformance.py +0 -0
  221. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_api_docs_builder.py +0 -0
  222. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_api_docs_paths.py +0 -0
  223. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_cli.py +0 -0
  224. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_cli_config_gen.py +0 -0
  225. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_cli_config_verify.py +0 -0
  226. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_cli_providers.py +0 -0
  227. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_cli_registry.py +0 -0
  228. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  229. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_cli_verify_strict.py +0 -0
  230. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  231. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_constants_config.py +0 -0
  232. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_entity_model.py +0 -0
  233. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_enum_conformance.py +0 -0
  234. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  235. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_extract_tier_collision.py +0 -0
  236. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_extractor_generator.py +0 -0
  237. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  238. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_format.py +0 -0
  239. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  240. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_fr010_output_codegen.py +0 -0
  241. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  242. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_generator.py +0 -0
  243. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_generator_extension_seams.py +0 -0
  244. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_golden.py +0 -0
  245. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  246. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_inheritance_conformance.py +0 -0
  247. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_instance_artifacts.py +0 -0
  248. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_m2m_codegen.py +0 -0
  249. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_origin_nullability.py +0 -0
  250. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_output_parser_generator.py +0 -0
  251. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_overwrite_policy.py +0 -0
  252. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_payload_vo_generator.py +0 -0
  253. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_project_config.py +0 -0
  254. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_projection_compile.py +0 -0
  255. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_render_helper_conformance.py +0 -0
  256. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_render_helper_generator.py +0 -0
  257. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_reverse_finders.py +0 -0
  258. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_router_autoset.py +0 -0
  259. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_router_generator.py +0 -0
  260. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_runner.py +0 -0
  261. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_sourceless_projection.py +0 -0
  262. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_template_data.py +0 -0
  263. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_template_generator.py +0 -0
  264. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_template_output_pattern.py +0 -0
  265. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_template_scope_helpers.py +0 -0
  266. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_template_scope_walk.py +0 -0
  267. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_template_spec.py +0 -0
  268. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_tph_codegen.py +0 -0
  269. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_trace_helper_generator.py +0 -0
  270. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_type_map.py +0 -0
  271. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_validation_conformance.py +0 -0
  272. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/codegen/test_write_through.py +0 -0
  273. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/__init__.py +0 -0
  274. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/conformance-expected-failures.json +0 -0
  275. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/conformance_adapter.py +0 -0
  276. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/corpus.py +0 -0
  277. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/expected_failures.py +0 -0
  278. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/fixture_discovery.py +0 -0
  279. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/navigator.py +0 -0
  280. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  281. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_conformance.py +0 -0
  282. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_extract_conformance.py +0 -0
  283. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_extract_object_verdict.py +0 -0
  284. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  285. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_fr011_attrs.py +0 -0
  286. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_generator_registry_conformance.py +0 -0
  287. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_object_model_conformance.py +0 -0
  288. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_provider_composition_conformance.py +0 -0
  289. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_registry_conformance.py +0 -0
  290. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_runner_hardfail.py +0 -0
  291. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  292. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_strict_attr_load.py +0 -0
  293. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_template_codegen_conformance.py +0 -0
  294. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_template_generator_conformance.py +0 -0
  295. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/test_yaml_conformance.py +0 -0
  296. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  297. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/__init__.py +0 -0
  298. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/api_contract_assertions.py +0 -0
  299. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/api_contract_jsonb_server.py +0 -0
  300. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/api_contract_m2m_server.py +0 -0
  301. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/api_contract_server.py +0 -0
  302. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/generated_jsonb_app.py +0 -0
  303. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/generated_m2m_app.py +0 -0
  304. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/generated_router_app.py +0 -0
  305. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/generated_tph_app.py +0 -0
  306. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/meta_ai_trace.yaml +0 -0
  307. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/normalization.py +0 -0
  308. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/postgres_container.py +0 -0
  309. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/query_runner.py +0 -0
  310. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/scenarios.py +0 -0
  311. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_api_contract.py +0 -0
  312. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_api_contract_generated.py +0 -0
  313. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_api_contract_jsonb.py +0 -0
  314. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_api_contract_m2m.py +0 -0
  315. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  316. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_api_contract_tph_generated.py +0 -0
  317. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  318. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_llm_call_trace.py +0 -0
  319. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_normalization.py +0 -0
  320. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_query_scenarios.py +0 -0
  321. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/integration/test_runtime_return_types.py +0 -0
  322. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/loader/__init__.py +0 -0
  323. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/loader/test_enum_extends_values_conflict.py +0 -0
  324. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/loader/test_template_payload_target_210.py +0 -0
  325. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/open_closed_proof_test.py +0 -0
  326. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/__init__.py +0 -0
  327. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/__init__.py +0 -0
  328. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_coerce.py +0 -0
  329. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_extract.py +0 -0
  330. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_extract_map.py +0 -0
  331. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  332. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_locate.py +0 -0
  333. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_model.py +0 -0
  334. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_normalize.py +0 -0
  335. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_strip.py +0 -0
  336. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  337. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/prompt/__init__.py +0 -0
  338. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/prompt/test_output_format_renderer.py +0 -0
  339. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/test_email_document.py +0 -0
  340. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/test_filesystem_provider.py +0 -0
  341. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/test_output_format_renderer_nested.py +0 -0
  342. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/test_output_prompt_conformance.py +0 -0
  343. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/test_render_conformance.py +0 -0
  344. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/test_render_max_chars.py +0 -0
  345. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/test_verify.py +0 -0
  346. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/render/test_verify_conformance.py +0 -0
  347. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/source/__init__.py +0 -0
  348. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/source/test_fr5c_merge_attribution.py +0 -0
  349. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/source/test_fr5d_reference_resolution.py +0 -0
  350. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/source/test_fr5e_database_source_shape.py +0 -0
  351. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/source/test_json_path.py +0 -0
  352. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/source/test_semantic_diff.py +0 -0
  353. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/source/test_source_on_node.py +0 -0
  354. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/source/test_yaml_positions.py +0 -0
  355. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/test_api_docs_accuracy.py +0 -0
  356. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/__init__.py +0 -0
  357. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_agent_context_staleness.py +0 -0
  358. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_capabilities.py +0 -0
  359. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_common_attrs.py +0 -0
  360. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_core_types.py +0 -0
  361. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_effective_package.py +0 -0
  362. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_errors.py +0 -0
  363. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_field_enum.py +0 -0
  364. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_field_map_validation.py +0 -0
  365. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  366. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  367. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_index_lookup.py +0 -0
  368. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_llm_recorder.py +0 -0
  369. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_loader.py +0 -0
  370. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_loader_bom.py +0 -0
  371. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_loader_class.py +0 -0
  372. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_merge.py +0 -0
  373. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_meta_attr.py +0 -0
  374. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_meta_data.py +0 -0
  375. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_meta_source.py +0 -0
  376. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_module_shortcuts.py +0 -0
  377. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_n2m_resolver.py +0 -0
  378. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  379. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  380. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  381. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_object_manager_write_through.py +0 -0
  382. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_one_primary_source.py +0 -0
  383. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_parser.py +0 -0
  384. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_parser_yaml.py +0 -0
  385. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_provider.py +0 -0
  386. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_provider_extension.py +0 -0
  387. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_registry.py +0 -0
  388. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_registry_completeness.py +0 -0
  389. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_registry_extend.py +0 -0
  390. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_registry_sealed.py +0 -0
  391. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_relationship_referential_actions.py +0 -0
  392. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_resolution_key.py +0 -0
  393. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  394. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_serializer.py +0 -0
  395. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_shared_constants.py +0 -0
  396. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_smoke.py +0 -0
  397. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_source_escapes.py +0 -0
  398. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_sources.py +0 -0
  399. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_strict_child_placement.py +0 -0
  400. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_super_resolve.py +0 -0
  401. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_template_toolcall.py +0 -0
  402. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  403. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_validation_attr_schema.py +0 -0
  404. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_validation_filter_values.py +0 -0
  405. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_validation_origin_195.py +0 -0
  406. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_validation_origin_paths.py +0 -0
  407. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_validation_sort_field.py +0 -0
  408. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_validation_warnings.py +0 -0
  409. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_write_coercion_arrays.py +0 -0
  410. {metaobjects-0.23.1 → metaobjects-0.23.2}/tests/unit/test_yaml_desugar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: metaobjects
3
- Version: 0.23.1
3
+ Version: 0.23.2
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.23.1"
3
+ version = "0.23.2"
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"
@@ -53,6 +53,11 @@ _OPS_STRING: tuple[str, ...] = ("eq", "ne", "in", "like", "isNull")
53
53
  # uuid: identity-comparison only — no `like` (not a substring type), no ordering.
54
54
  _OPS_UUID: tuple[str, ...] = ("eq", "ne", "in", "isNull")
55
55
  _OPS_NUMERIC: tuple[str, ...] = ("eq", "ne", "gt", "gte", "lt", "lte", "in", "isNull")
56
+ # int-backed enum (@intValueMap, design D5): the enum band minus `like` — the column
57
+ # is an integer, and a substring match against an integer is meaningless. Distinct
58
+ # constant from _OPS_UUID despite the identical members: they narrow for different
59
+ # reasons and would diverge independently.
60
+ _OPS_ENUM_INT_BACKED: tuple[str, ...] = ("eq", "ne", "in", "isNull")
56
61
  _OPS_BOOLEAN: tuple[str, ...] = ("eq", "isNull")
57
62
 
58
63
 
@@ -118,6 +123,36 @@ def ops_for_subtype_ordered(sub_type: str | None) -> tuple[str, ...]:
118
123
  return ()
119
124
 
120
125
 
126
+ def ops_for_field_ordered(field: MetaField) -> tuple[str, ...]:
127
+ """The operator tuple for a FIELD, in canonical operator order.
128
+
129
+ The ordered counterpart of ``validation_passes.ops_for_field``, and the entry
130
+ point for every consumer that has a field in hand: the codegen allowlist emit
131
+ and the cross-port ``field.filter-ops`` conformance capability.
132
+
133
+ Identical to :func:`ops_for_subtype_ordered` except for ONE case: an int-backed
134
+ ``field.enum`` (one declaring ``@intValueMap``, design D5) persists as an INTEGER
135
+ column, so ``like`` -- a substring match -- is dropped. ``eq``/``ne``/``in``
136
+ survive because the member symbol encodes to its integer before it reaches SQL;
137
+ ``like`` has no such encoding, and an unencoded ``LIKE 'DRAFT'`` against an integer
138
+ column is a request-time type error.
139
+
140
+ ADR-0039: the ``@intValueMap`` read is RESOLVING (``attrs()``, NOT ``attr()`` --
141
+ Python inverts the TS naming, ``attr()`` here is own-only). Post-#246 the map lives
142
+ on a shared root-level abstract declaration and consuming fields INHERIT it, so an
143
+ own-only read would see it absent on exactly the shape adopters are steered toward
144
+ and wrongly keep ``like``.
145
+
146
+ Cross-port: ``fixtures/conformance/filter-ops-matrix`` pins ``fEnum`` vs
147
+ ``fEnumInt`` in all five ports.
148
+ """
149
+ if field.sub_type == fc.FIELD_SUBTYPE_ENUM and isinstance(
150
+ field.attrs().get(fc.FIELD_ATTR_INT_VALUE_MAP), dict
151
+ ):
152
+ return _OPS_ENUM_INT_BACKED
153
+ return ops_for_subtype_ordered(field.sub_type)
154
+
155
+
121
156
  def _is_filterable(field: MetaField) -> bool:
122
157
  """True iff ``field`` carries ``@filterable: true`` as a metadata attribute.
123
158
 
@@ -166,10 +201,10 @@ class FilterAllowlistGenerator:
166
201
  name = "filter-allowlist-generator"
167
202
 
168
203
  def _ops_for_field(self, field: MetaField) -> tuple[str, ...]:
169
- """The operator tuple allowed for ``field`` (FR-009 §5 per-subtype matrix by
170
- default). Override to customize the per-field operator vocabulary; returning
171
- an empty tuple excludes the field from the allowlist."""
172
- return ops_for_subtype_ordered(field.sub_type)
204
+ """The operator tuple allowed for ``field`` (FR-009 §5 matrix by default).
205
+ Override to customize the per-field operator vocabulary; returning an empty
206
+ tuple excludes the field from the allowlist."""
207
+ return ops_for_field_ordered(field)
173
208
 
174
209
  def _compute_filterable_ops(
175
210
  self, entity: MetaObject, object_index: dict[str, MetaObject] | None = None
@@ -9,6 +9,7 @@ from .meta.core.attr.attr_constants import (
9
9
  ATTR_SUBTYPE_EXPRESSION,
10
10
  ATTR_SUBTYPE_FILTER,
11
11
  ATTR_SUBTYPE_INT,
12
+ ATTR_SUBTYPE_INT_MAP,
12
13
  ATTR_SUBTYPE_STRING,
13
14
  ATTR_SUBTYPES,
14
15
  )
@@ -19,6 +20,7 @@ from .meta.core.field.field_constants import (
19
20
  FIELD_ATTR_AUTO_SET,
20
21
  FIELD_ATTR_CURRENCY,
21
22
  FIELD_ATTR_DEFAULT,
23
+ FIELD_ATTR_INT_VALUE_MAP,
22
24
  FIELD_ATTR_PROVIDED,
23
25
  FIELD_ATTR_MAX_LENGTH,
24
26
  FIELD_ATTR_OBJECT_REF,
@@ -472,6 +474,17 @@ core_provider.add(
472
474
  value_type=ATTR_SUBTYPE_BOOLEAN,
473
475
  required=False,
474
476
  ),
477
+ # int-backed-enum-values plan — @intValueMap: optional per-member int
478
+ # values ({member: int}) switching this enum field's DB persistence
479
+ # from string+CHECK to integer+CHECK. Keys must exactly match @values;
480
+ # values must be unique integers (enforced in _validate_enum_values,
481
+ # Rule 4). Structural (core), like @values / @provided — not a
482
+ # TS-web-only / prompt-domain overlay.
483
+ AttrSchema(
484
+ name=FIELD_ATTR_INT_VALUE_MAP,
485
+ value_type=ATTR_SUBTYPE_INT_MAP,
486
+ required=False,
487
+ ),
475
488
  # FR-033 — the field.enum tolerant-extract overlays (@enumAlias / @enumDoc /
476
489
  # @coerceDefault / @normalize) are NO LONGER declared here; they are re-homed
477
490
  # to the prompt domain provider (`prompt_provider`, metaobjects-prompt) via
@@ -194,6 +194,12 @@ class ErrorCode(str, Enum):
194
194
  # own @values would be silently dropped in codegen. Remove the own @values
195
195
  # to inherit the shared set, or extend a concrete (non-shared) enum instead.
196
196
  ERR_ENUM_EXTENDS_VALUES_CONFLICT = "ERR_ENUM_EXTENDS_VALUES_CONFLICT"
197
+ # A field.enum carries @intValueMap together with isArray=true. Int-backing is
198
+ # a persistence-layer codec and no port implements it element-wise over an
199
+ # array column, so the combination would silently persist member SYMBOLS into
200
+ # an integer array. An array-of-enum stays string-backed: drop @intValueMap,
201
+ # or make the field scalar.
202
+ ERR_ENUM_INT_VALUE_MAP_ARRAY = "ERR_ENUM_INT_VALUE_MAP_ARRAY"
197
203
  ERR_UNKNOWN = "ERR_UNKNOWN"
198
204
 
199
205
 
@@ -23,6 +23,7 @@ from ..meta.core.field.field_constants import (
23
23
  ENUM_MEMBER_PATTERN,
24
24
  FIELD_ATTR_COERCE_DEFAULT,
25
25
  FIELD_ATTR_DEFAULT,
26
+ FIELD_ATTR_INT_VALUE_MAP,
26
27
  FIELD_ATTR_OBJECT_REF,
27
28
  FIELD_ATTR_REQUIRED,
28
29
  FIELD_ATTR_STORAGE,
@@ -60,6 +61,7 @@ from ..meta.persistence.source.source_constants import (
60
61
  SOURCE_ROLE_PRIMARY,
61
62
  )
62
63
  from ..meta.core.attr.attr_constants import (
64
+ ATTR_SUBTYPE_INT_MAP,
63
65
  ATTR_SUBTYPE_PROPERTIES,
64
66
  ATTR_SUBTYPE_STRINGARRAY,
65
67
  )
@@ -281,6 +283,26 @@ def _type_ok(value: object, value_type: str) -> bool:
281
283
  # fails here → ERR_BAD_ATTR_VALUE, matching TS + Java (fail-closed); an object
282
284
  # @expr then flows to the closed-grammar check in the origin pass.
283
285
  return isinstance(value, dict)
286
+ if value_type == ATTR_SUBTYPE_INT_MAP:
287
+ # attr.intMap (int-backed-enum-values plan): an object whose every member
288
+ # value is an integer (e.g. field.enum's @intValueMap). This port has no
289
+ # per-attr-class validateValue dispatch (that's TS's architecture); the
290
+ # generic shape check lives here, mirroring C#'s ValueMatchesType
291
+ # ATTR_SUBTYPE_INT_MAP case. field.enum's own key-set/uniqueness content
292
+ # rules run separately in _validate_enum_values (Rule 4), which skips
293
+ # re-reporting a non-integer member already caught here.
294
+ #
295
+ # Final-review fix: also bound every value to the 32-bit signed int range
296
+ # (inclusive) — the eventual DB column for an int-backed enum is a 32-bit
297
+ # Postgres/SQLite `integer` (design doc D5), matching Java's
298
+ # IntMapAttribute#setValueAsString bound check exactly. Python ints have
299
+ # no fixed width, so this is the only place that boundary is enforced.
300
+ return isinstance(value, dict) and all(
301
+ isinstance(v, int)
302
+ and not isinstance(v, bool)
303
+ and -2147483648 <= v <= 2147483647
304
+ for v in value.values()
305
+ )
284
306
  # Unknown value types (e.g. "class") — allow anything.
285
307
  return True
286
308
 
@@ -544,6 +566,32 @@ def _validate_attr_schema(
544
566
  _ENUM_MEMBER_RE = re.compile(ENUM_MEMBER_PATTERN)
545
567
 
546
568
 
569
+ def _shared_enum_super(node: MetaData) -> MetaData | None:
570
+ """The shared-enum super of ``node``, or ``None`` when it has none.
571
+
572
+ "Shared" (FR-019 / #246) means the immediate super is abstract AND declared at
573
+ metadata-root — its parent is the ``metadata.root`` node, not an object. Such a
574
+ declaration is materialized ONCE per port as a single named type, so anything a
575
+ consuming field re-declares that belongs to the shared TYPE's contract (its
576
+ ``@values`` member set, or the ``@intValueMap`` integer backing of that set) is a
577
+ conflict. A concrete super, or a non-root abstract super (e.g. one nested inside
578
+ an object), is legal and not flagged.
579
+
580
+ Immediate-super-only, matching codegen's ``resolve_shared_enum_decl``
581
+ (``codegen/generators/fr019_shared_enum.py``) so the validator and the
582
+ shared-enum collapse agree on what "shared" means.
583
+ """
584
+ sup = node.super_data
585
+ if (
586
+ sup is not None
587
+ and sup.is_abstract
588
+ and sup.parent is not None
589
+ and sup.parent.type == TYPE_METADATA
590
+ ):
591
+ return sup
592
+ return None
593
+
594
+
547
595
  def _validate_enum_values(
548
596
  root: MetaData,
549
597
  errors: list[MetaError],
@@ -556,6 +604,21 @@ def _validate_enum_values(
556
604
  # @coerceDefault / @default / @normalize while inheriting @values).
557
605
  _validate_enum_fr011_attrs(node, errors)
558
606
 
607
+ # Rule 4 (@intValueMap content rules) is independent of whether @values is
608
+ # own or inherited on THIS node — it must run whenever this node owns
609
+ # @intValueMap, using the EFFECTIVE @values (own-or-inherited) as the
610
+ # membership set. Mirrors TS/C#/Java, which all check this unconditionally
611
+ # (Java's `validateEnumIntValueMap` is a standalone call at the top of
612
+ # `validateEnumNode`). Deliberately called BEFORE the `own_values is None:
613
+ # continue` gate below so a concrete field.enum that inherits @values via
614
+ # `extends` but declares its own @intValueMap is still validated.
615
+ _validate_enum_int_value_map(node, errors)
616
+
617
+ # Design D7, narrowed: int-backing is scalar-only. Separate from the content
618
+ # rules above because it must fire on the node that combines the two halves,
619
+ # which is NOT necessarily the node that declares the map.
620
+ _validate_enum_int_value_map_not_array(node, errors)
621
+
559
622
  # ADR-0039 sanctioned own: validates the AUTHORED @values membership on THIS
560
623
  # node (mirrors the TS attr-schema-validate `node.ownAttrs()`); an inherited
561
624
  # @values yields None here and is validated on its declaring node.
@@ -607,19 +670,9 @@ def _validate_enum_values(
607
670
 
608
671
  # #246: own @values AND extends a shared package-level abstract enum —
609
672
  # one shared enum type has one member set, so the own @values would be
610
- # silently dropped by the shared-enum codegen collapse. "Shared" means
611
- # the resolved super is abstract AND declared at metadata-root (its
612
- # parent is the metadata.root node, not nested inside an object) — a
613
- # concrete super, or a non-root abstract super (e.g. nested inside an
614
- # object), is legal and not flagged. Mirrors the TS reference
615
- # (attr-schema-validate.ts).
616
- sup = node.super_data
617
- if (
618
- sup is not None
619
- and sup.is_abstract
620
- and sup.parent is not None
621
- and sup.parent.type == TYPE_METADATA
622
- ):
673
+ # silently dropped by the shared-enum codegen collapse. Mirrors the TS
674
+ # reference (attr-schema-validate.ts).
675
+ if _shared_enum_super(node) is not None:
623
676
  errors.append(
624
677
  MetaError(
625
678
  f"{label} declares its own '@{FIELD_ATTR_VALUES}' but extends "
@@ -633,6 +686,113 @@ def _validate_enum_values(
633
686
  )
634
687
 
635
688
 
689
+ def _validate_enum_int_value_map_not_array(node: MetaData, errors: list[MetaError]) -> None:
690
+ """``@intValueMap`` is scalar-only (design D7, narrowed).
691
+
692
+ Int-backing is a persistence-layer CODEC, and no port implements it
693
+ element-wise over an array column: this port's ``ObjectManager`` would bind the
694
+ symbol LIST straight into an ``integer[]``, OMDB's ``EnumCodec`` and Kotlin's
695
+ ``customEnumeration`` are scalar by construction, and TypeScript's sqlite branch
696
+ serializes an array as JSON text before the enum case is reached. Two ports that
697
+ happen to compose (TS/Postgres, C#) are not a feature — shipping a claim four
698
+ ports silently get wrong is the ``field.byte``/``short``/``class`` mistake.
699
+
700
+ BOTH halves are read RESOLVING, unlike the content rules: the illegal thing is
701
+ the EFFECTIVE combination. Post-#246 the map must live on the shared abstract
702
+ declaration, so the field that inherits it is exactly where ``isArray`` gets
703
+ declared — an own-only read would see the two halves on different nodes and
704
+ never fire.
705
+ """
706
+ if not isinstance(node.get_meta_attr(FIELD_ATTR_INT_VALUE_MAP), dict):
707
+ return
708
+ if not node.resolved_is_array():
709
+ return
710
+ errors.append(
711
+ MetaError(
712
+ f"{_node_label(node)} declares '@{FIELD_ATTR_INT_VALUE_MAP}' with isArray=true; "
713
+ f"int-backing is scalar-only — an array-of-enum persists its member symbols. "
714
+ f"Remove '@{FIELD_ATTR_INT_VALUE_MAP}', or make the field scalar.",
715
+ ErrorCode.ERR_ENUM_INT_VALUE_MAP_ARRAY,
716
+ envelope=node.source,
717
+ )
718
+ )
719
+
720
+
721
+ def _validate_enum_int_value_map(node: MetaData, errors: list[MetaError]) -> None:
722
+ """``@intValueMap`` content rules (optional), independent of whether ``@values``
723
+ is own or inherited on this node. Mirrors TS/C#/Java, which all run this check
724
+ unconditionally against the node's EFFECTIVE ``@values`` — a concrete
725
+ ``field.enum`` that inherits ``@values`` via ``extends`` but declares its own
726
+ ``@intValueMap`` locally must still have that map validated.
727
+
728
+ a. Key set must exactly match the EFFECTIVE ``@values`` (own or inherited).
729
+ b. No two members may share the same int (protobuf's stance — no alias
730
+ opt-in). Every-value-is-an-integer is already enforced by ``_type_ok``'s
731
+ ATTR_SUBTYPE_INT_MAP case at parse time.
732
+ """
733
+ int_value_map = node.attr(FIELD_ATTR_INT_VALUE_MAP)
734
+ if not isinstance(int_value_map, dict):
735
+ return
736
+
737
+ label = _node_label(node)
738
+
739
+ # #246 (int-backed twin): the symbol->int mapping is a property of the enum
740
+ # VOCABULARY, not of one column that uses it -- it is @values' numeric half. A
741
+ # shared enum is materialized once as a single type, so a per-field map would
742
+ # give one logical type N storage encodings (and, where a port emits per-TYPE
743
+ # codec artifacts, two same-named declarations). Same remedy as the @values
744
+ # half: declare it on the shared declaration and inherit it.
745
+ shared_super = _shared_enum_super(node)
746
+ if shared_super is not None:
747
+ errors.append(
748
+ MetaError(
749
+ f"{label} declares its own '@{FIELD_ATTR_INT_VALUE_MAP}' but extends "
750
+ f"a shared package-level abstract enum — a shared enum's integer "
751
+ f"backing is owned by the shared declaration. Move "
752
+ f"'@{FIELD_ATTR_INT_VALUE_MAP}' onto the shared declaration to "
753
+ f"inherit it, or extend a concrete (non-shared) enum instead",
754
+ ErrorCode.ERR_ENUM_EXTENDS_VALUES_CONFLICT,
755
+ envelope=node.source,
756
+ )
757
+ )
758
+ effective_values = _effective_enum_values(node)
759
+ member_set = set(effective_values)
760
+ key_set = set(int_value_map.keys())
761
+ missing = [m for m in effective_values if m not in key_set]
762
+ extra = [k for k in int_value_map if k not in member_set]
763
+ if missing or extra:
764
+ parts = []
765
+ if missing:
766
+ parts.append(f"missing: {', '.join(missing)}")
767
+ if extra:
768
+ parts.append(f"unknown: {', '.join(extra)}")
769
+ errors.append(
770
+ MetaError(
771
+ f"{label} attribute '@{FIELD_ATTR_INT_VALUE_MAP}' keys must exactly match "
772
+ f"'@{FIELD_ATTR_VALUES}' members ({'; '.join(parts)}).",
773
+ ErrorCode.ERR_BAD_ATTR_VALUE,
774
+ envelope=node.source,
775
+ )
776
+ )
777
+
778
+ seen_values: dict[int, str] = {}
779
+ for member, value in int_value_map.items():
780
+ if not isinstance(value, int) or isinstance(value, bool):
781
+ continue # _type_ok already reported this
782
+ owner = seen_values.get(value)
783
+ if owner is not None:
784
+ errors.append(
785
+ MetaError(
786
+ f"{label} attribute '@{FIELD_ATTR_INT_VALUE_MAP}' members {owner!r} and {member!r} "
787
+ f"share the same value {value}; every member must have a unique int.",
788
+ ErrorCode.ERR_BAD_ATTR_VALUE,
789
+ envelope=node.source,
790
+ )
791
+ )
792
+ else:
793
+ seen_values[value] = member
794
+
795
+
636
796
  def _effective_enum_values(node: MetaData) -> list[str]:
637
797
  """The effective ``@values`` members of an enum node (own or inherited via
638
798
  ``extends:``). Empty list when absent. Mirrors Java ``effectiveEnumValues``."""
@@ -913,6 +1073,45 @@ def ops_for_subtype(field_subtype: str) -> frozenset[str]:
913
1073
  return frozenset()
914
1074
 
915
1075
 
1076
+ # The int-backed-enum band: the enum band minus `like`. Named so the narrowing is
1077
+ # one constant rather than a set rebuilt at every call.
1078
+ _OPS_ENUM_INT_BACKED: frozenset[str] = frozenset({"eq", "ne", "in", "isNull"})
1079
+
1080
+
1081
+ def ops_for_field(field: MetaData) -> frozenset[str]:
1082
+ """The filter-operator band for a FIELD.
1083
+
1084
+ The entry point every consumer that has a field in hand must use (loader
1085
+ validation, the codegen filter-allowlist generator, the cross-port
1086
+ ``field.filter-ops`` capability).
1087
+
1088
+ Identical to :func:`ops_for_subtype` except for ONE case: an int-backed
1089
+ ``field.enum`` (one declaring ``@intValueMap``, design D5) persists as an
1090
+ INTEGER column, so ``like`` -- a substring match -- is dropped.
1091
+ ``eq``/``ne``/``in`` survive because the member symbol encodes to its integer
1092
+ before it reaches SQL; ``like`` has no such encoding, and an unencoded
1093
+ ``LIKE 'DRAFT'`` against an integer column is a request-time type error.
1094
+
1095
+ ``ops_for_subtype`` cannot express this -- it only ever sees the subtype
1096
+ ``"enum"`` -- and is deliberately left unchanged for the one caller that
1097
+ genuinely has no field: the expression grammar's declared operand type.
1098
+
1099
+ ADR-0039: the ``@intValueMap`` read is RESOLVING (``attrs()``, NOT ``attr()``
1100
+ -- Python inverts the TS naming, ``attr()`` here is own-only). Post-#246 the
1101
+ map lives on a shared root-level abstract declaration and consuming fields
1102
+ INHERIT it, so an own-only read would see it absent on exactly the shape
1103
+ adopters are steered toward and wrongly keep ``like``.
1104
+
1105
+ Cross-port: ``fixtures/conformance/filter-ops-matrix`` pins ``fEnum`` vs
1106
+ ``fEnumInt`` in all five ports.
1107
+ """
1108
+ if field.sub_type == FIELD_SUBTYPE_ENUM and isinstance(
1109
+ field.attrs().get(FIELD_ATTR_INT_VALUE_MAP), dict
1110
+ ):
1111
+ return _OPS_ENUM_INT_BACKED
1112
+ return ops_for_subtype(field.sub_type)
1113
+
1114
+
916
1115
  # ---------------------------------------------------------------------------
917
1116
  # #195 — attr.expression closed grammar (validate + infer)
918
1117
  # ---------------------------------------------------------------------------
@@ -1095,8 +1294,10 @@ def _validate_datagrid_filter_values(
1095
1294
  continue
1096
1295
 
1097
1296
  # Build filterable map: field_name → allowed ops set
1297
+ # ops_for_field, not ops_for_subtype — an int-backed field.enum
1298
+ # (@intValueMap) stores as an integer, so `like` is not in its band.
1098
1299
  filterable: dict[str, frozenset[str]] = {
1099
- f.name: ops_for_subtype(f.sub_type)
1300
+ f.name: ops_for_field(f)
1100
1301
  for f in node.fields()
1101
1302
  if f.attrs().get("filterable") is True
1102
1303
  }
@@ -14,6 +14,13 @@ ATTR_SUBTYPE_CLASS = "class"
14
14
  # ATTR_SUBTYPE_EXPRESSION / meta-attr-expression.ts.
15
15
  ATTR_SUBTYPE_EXPRESSION = "expression"
16
16
 
17
+ # attr.intMap (int-backed-enum-values plan) — an object-shaped attribute whose
18
+ # values are all integers (e.g. field.enum's @intValueMap: {memberSymbol: int}).
19
+ # Generic shape check only; field.enum layers its own semantic rules (key-set
20
+ # membership, uniqueness) in its own content-rule validation pass. Mirrors TS
21
+ # ATTR_SUBTYPE_INT_MAP / C# AttrConstants.ATTR_SUBTYPE_INT_MAP.
22
+ ATTR_SUBTYPE_INT_MAP = "intMap"
23
+
17
24
  # The retired "stringarray" array attr subtype. It is NO LONGER a registered
18
25
  # (attr, sub_type) (not in ATTR_SUBTYPES) — array-ness is modeled as a "string"
19
26
  # attr with the orthogonal AttrSchema.is_array flag (matching Java's
@@ -33,4 +40,5 @@ ATTR_SUBTYPES = (
33
40
  ATTR_SUBTYPE_PROPERTIES,
34
41
  ATTR_SUBTYPE_CLASS,
35
42
  ATTR_SUBTYPE_EXPRESSION,
43
+ ATTR_SUBTYPE_INT_MAP,
36
44
  )
@@ -12,6 +12,7 @@ from .attr_constants import (
12
12
  ATTR_SUBTYPE_EXPRESSION,
13
13
  ATTR_SUBTYPE_FILTER,
14
14
  ATTR_SUBTYPE_INT,
15
+ ATTR_SUBTYPE_INT_MAP,
15
16
  ATTR_SUBTYPE_LONG,
16
17
  ATTR_SUBTYPE_PROPERTIES,
17
18
  ATTR_SUBTYPE_STRING,
@@ -134,6 +135,21 @@ class PropertiesAttr(MetaAttr):
134
135
  return DataType.OBJECT
135
136
 
136
137
 
138
+ class IntMapAttr(MetaAttr):
139
+ """attr.intMap (int-backed-enum-values plan) — an object-shaped attribute
140
+ whose values are all integers (e.g. field.enum's @intValueMap). Generic
141
+ shape check only; the deeper "every value is an int" check is enforced by
142
+ `_type_ok`'s ATTR_SUBTYPE_INT_MAP case in validation_passes.py (this port
143
+ has no per-attr-class validate_value dispatch — TS's is the one port that
144
+ does; C# mirrors the same "static type-check function" architecture this
145
+ port uses). field.enum's own key-set/uniqueness content rules run in a
146
+ separate pass. Mirrors TS IntMapAttr / C# ATTR_SUBTYPE_INT_MAP shape."""
147
+
148
+ @property
149
+ def data_type(self) -> DataType:
150
+ return DataType.OBJECT
151
+
152
+
137
153
  class ClassAttr(MetaAttr):
138
154
  @property
139
155
  def data_type(self) -> DataType:
@@ -145,4 +161,5 @@ register_attr_class(ATTR_SUBTYPE_STRINGARRAY, StringArrayAttr)
145
161
  register_attr_class(ATTR_SUBTYPE_FILTER, FilterAttr)
146
162
  register_attr_class(ATTR_SUBTYPE_PROPERTIES, PropertiesAttr)
147
163
  register_attr_class(ATTR_SUBTYPE_EXPRESSION, ExpressionAttr)
164
+ register_attr_class(ATTR_SUBTYPE_INT_MAP, IntMapAttr)
148
165
  register_attr_class(ATTR_SUBTYPE_CLASS, ClassAttr)
@@ -150,3 +150,10 @@ SORT_ORDER_VALUES = (SORT_ORDER_ASC, SORT_ORDER_DESC)
150
150
  # Regex pattern for enum member symbols — must be identifier-safe.
151
151
  # Cross-language contract: every port enforces this pattern.
152
152
  ENUM_MEMBER_PATTERN = r"^[A-Za-z_][A-Za-z0-9_]*$"
153
+
154
+ # int-backed-enum-values plan — @intValueMap (field.enum only): optional
155
+ # per-member int values ({member: int}) switching this enum field's DB
156
+ # persistence from string+CHECK to integer+CHECK. Keys must exactly match
157
+ # @values; values must be unique integers. The generated native type and wire
158
+ # format are unchanged in every language. Mirrors TS FIELD_ATTR_INT_VALUE_MAP.
159
+ FIELD_ATTR_INT_VALUE_MAP = "intValueMap"
@@ -311,6 +311,8 @@ class ObjectManager:
311
311
  }
312
312
  row = result.rows[0]
313
313
  mapped = {col_to_field.get(k, k): v for k, v in row.items()}
314
+ # int-backed enums come back as integers; hand the caller the symbol.
315
+ mapped = _decode_read_row({f.name: f for f in returning_fields}, mapped)
314
316
  # #214: a write-through entity's INSERT RETURNING covers only the table (non-derived)
315
317
  # columns; re-read the persisted row through the replica VIEW by PK so the returned row
316
318
  # carries the derived origin.* fields (read-your-writes). find_by_id routes to the view.
@@ -423,6 +425,8 @@ class ObjectManager:
423
425
  }
424
426
  row = result.rows[0]
425
427
  mapped = {col_to_field.get(k, k): v for k, v in row.items()}
428
+ # int-backed enums come back as integers; hand the caller the symbol.
429
+ mapped = _decode_read_row({f.name: f for f in returning_fields}, mapped)
426
430
  # #214: re-read the updated row through the replica VIEW by PK so the returned row
427
431
  # carries the derived origin.* fields (write targeted the table, which lacks them).
428
432
  if entity.is_write_through():
@@ -509,8 +513,11 @@ class ObjectManager:
509
513
  self.last_column_oids = {
510
514
  col_to_field.get(c, c): oid for c, oid in result.column_oids.items()
511
515
  }
516
+ # int-backed enums come back as integers; hand the caller the symbol.
517
+ fields_by_name = {f.name: f for f in entity.fields()}
512
518
  return [
513
- {col_to_field.get(k, k): v for k, v in row.items()} for row in result.rows
519
+ _decode_read_row(fields_by_name, {col_to_field.get(k, k): v for k, v in row.items()})
520
+ for row in result.rows
514
521
  ]
515
522
 
516
523
  def count(self, entity_name: str, filter: Filter | None = None) -> int:
@@ -711,12 +718,13 @@ def _compile_filter(f: Filter | None, entity: MetaObject) -> tuple[str, list[Any
711
718
  mf = entity.find_field(field_name)
712
719
  col = _column_of(mf) if mf is not None else field_name
713
720
  if not isinstance(ops, dict):
714
- # Shortcut: {field: value} → equality
721
+ # Shortcut: {field: value} → equality. Encoded like any other bound
722
+ # value (see _op_clause) so an int-backed enum reaches SQL as its int.
715
723
  parts.append(f"{_q(col)} = %s")
716
- params.append(ops)
724
+ params.append(_coerce_write_value(mf, ops) if mf is not None else ops)
717
725
  continue
718
726
  for op, value in ops.items():
719
- sql, p = _op_clause(col, op, value)
727
+ sql, p = _op_clause(col, op, value, mf)
720
728
  parts.append(sql)
721
729
  params.extend(p)
722
730
  if not parts:
@@ -724,9 +732,21 @@ def _compile_filter(f: Filter | None, entity: MetaObject) -> tuple[str, list[Any
724
732
  return " AND ".join(parts), params
725
733
 
726
734
 
727
- def _op_clause(col: str, op: str, value: Any) -> tuple[str, list[Any]]:
728
- """Translate one operator → SQL + params. Mirrors TS/C#/Java semantics."""
735
+ def _op_clause(col: str, op: str, value: Any, field: MetaField | None = None) -> tuple[str, list[Any]]:
736
+ """Translate one operator → SQL + params. Mirrors TS/C#/Java semantics.
737
+
738
+ Bound values go through the WRITE codec, exactly as an INSERT's do: the four
739
+ sibling ports all encode on this path (TS through the Drizzle customType, Java
740
+ through GenericSQLDriver.setStatementValue → EnumCodec.write, Kotlin through
741
+ Exposed's toDb, C# through the EF converter). Without it an int-backed enum's
742
+ filter binds the member SYMBOL against an INTEGER column — the filter band
743
+ keeps eq/ne/in for int-backed enums precisely BECAUSE the symbol is supposed
744
+ to encode to its integer before reaching SQL.
745
+ """
729
746
  qc = _q(col)
747
+ if field is not None and op != "isNull":
748
+ value = ([_coerce_write_value(field, v) for v in value]
749
+ if op == "in" and value else _coerce_write_value(field, value))
730
750
  if op == "eq": return f"{qc} = %s", [value]
731
751
  if op == "ne": return f"{qc} <> %s", [value]
732
752
  if op == "gt": return f"{qc} > %s", [value]
@@ -823,11 +843,79 @@ def _coerce_write_value(field: MetaField, value: Any) -> Any:
823
843
  storage = field.get_meta_attr(fc.FIELD_ATTR_STORAGE) # ADR-0039 resolving
824
844
  if storage != "flattened": # None / "jsonb" / "subdocument" → single jsonb column
825
845
  return _json.dumps(value)
846
+ # field.enum carrying @intValueMap: the column is an INTEGER while the native
847
+ # and wire contract stays the member SYMBOL (int-backing is a persistence-layer
848
+ # concern, invisible above this codec), so encode symbol -> declared int here.
849
+ #
850
+ # An unmapped symbol is passed through untouched: membership is the column's
851
+ # CHECK constraint to enforce, and inventing a value here would hide the drift.
852
+ if sub == fc.FIELD_SUBTYPE_ENUM:
853
+ int_map = _int_value_map(field)
854
+ if int_map is not None and value in int_map:
855
+ return int_map[value]
856
+
826
857
  # Everything else (string / int / long / double / float / boolean / enum)
827
858
  # is already the native type pg8000 binds directly.
828
859
  return value
829
860
 
830
861
 
862
+ def _int_value_map(field: MetaField) -> dict[Any, Any] | None:
863
+ """The declared ``@intValueMap`` (symbol → int), or ``None`` when the enum is
864
+ string-backed.
865
+
866
+ ADR-0039 resolving: the map is ``@values``' numeric half — a logical property
867
+ of the enum vocabulary that inherits through ``extends`` — so it is read with
868
+ ``get_meta_attr``, NOT the own-only accessor (contrast ``@dbColumnType`` in
869
+ :func:`_coerce_write_value`, the one field attribute that is deliberately
870
+ own-only). Shared by the write and read halves of the enum codec, mirroring
871
+ the dedicated int-map helper each sibling port's codec keeps (Java
872
+ ``EnumCodec.intValueMap``, Kotlin ``readIntValueMap``, C# ``IntValueMapOf``).
873
+ """
874
+ m = field.get_meta_attr(fc.FIELD_ATTR_INT_VALUE_MAP)
875
+ return m if isinstance(m, dict) else None
876
+
877
+
878
+ def _decode_read_value(field: MetaField, value: Any) -> Any:
879
+ """Decode a stored value back to its authoring form on read.
880
+
881
+ The inverse of :func:`_coerce_write_value`'s int-backed-enum branch, and
882
+ today its only case: the column holds the member's integer, callers expect
883
+ the member SYMBOL. Every other field subtype is returned verbatim, keeping
884
+ ADR-0019's "runtime returns native in-process types" contract intact.
885
+
886
+ An int with no member RAISES. The database then holds a value the model says
887
+ is impossible — a hand-written INSERT, or a member removed without a
888
+ migration — and neither alternative is honest: returning the raw int hands
889
+ the caller a "member" that is not one (C#, Kotlin and TypeScript type this
890
+ property as a CLOSED enum, where ``7`` is not even representable), and
891
+ returning ``None`` hides the corruption behind a nullable column. Every port
892
+ throws here.
893
+ """
894
+ if value is None:
895
+ return None
896
+ if field.sub_type != fc.FIELD_SUBTYPE_ENUM:
897
+ return value
898
+ int_map = _int_value_map(field)
899
+ if int_map is None or not isinstance(value, int) or isinstance(value, bool):
900
+ return value
901
+ for symbol, stored in int_map.items():
902
+ if stored == value:
903
+ return symbol
904
+ raise ValueError(
905
+ f"field.enum '{field.name}' read stored value {value} with no member in "
906
+ f"@{fc.FIELD_ATTR_INT_VALUE_MAP} (declared: {int_map}) — the database holds "
907
+ f"a value the model does not describe."
908
+ )
909
+
910
+
911
+ def _decode_read_row(fields_by_name: dict[str, MetaField], row: dict[str, Any]) -> dict[str, Any]:
912
+ """Apply :func:`_decode_read_value` to every field-keyed value in a mapped row."""
913
+ return {
914
+ k: (_decode_read_value(fields_by_name[k], v) if k in fields_by_name else v)
915
+ for k, v in row.items()
916
+ }
917
+
918
+
831
919
  # ----------------------------------------------------------------------------
832
920
  # #203 — @autoSet CRUD stamping. A ``field.timestamp`` (or any temporal field)
833
921
  # marked ``@autoSet: onCreate|onUpdate`` declares "the runtime owns this
@@ -19,6 +19,12 @@
19
19
  "dataType": "int",
20
20
  "description": "A 32-bit-integer-valued metadata attribute. Coerces to and validates as a number."
21
21
  },
22
+ {
23
+ "type": "attr",
24
+ "subType": "intMap",
25
+ "dataType": "object",
26
+ "description": "An object-shaped attribute whose values are all integers (e.g. field.enum's @intValueMap: {memberSymbol: int}). Generic shape check only; a consumer field type layers its own semantic rules (key-set membership, uniqueness) in its own content-rule validation."
27
+ },
22
28
  {
23
29
  "type": "attr",
24
30
  "subType": "long",