metaobjects 0.24.0__tar.gz → 0.24.1__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 (425) hide show
  1. {metaobjects-0.24.0 → metaobjects-0.24.1}/PKG-INFO +1 -1
  2. {metaobjects-0.24.0 → metaobjects-0.24.1}/pyproject.toml +1 -1
  3. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/core_types.py +10 -2
  4. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/errors.py +17 -0
  5. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validation_passes.py +308 -33
  6. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/registry_manifest.py +1 -1
  7. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/identity.json +1 -1
  8. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/index.json +1 -1
  9. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/origin.json +5 -5
  10. {metaobjects-0.24.0 → metaobjects-0.24.1}/uv.lock +1 -1
  11. {metaobjects-0.24.0 → metaobjects-0.24.1}/.gitignore +0 -0
  12. {metaobjects-0.24.0 → metaobjects-0.24.1}/LICENSE +0 -0
  13. {metaobjects-0.24.0 → metaobjects-0.24.1}/README.md +0 -0
  14. {metaobjects-0.24.0 → metaobjects-0.24.1}/hatch_build.py +0 -0
  15. {metaobjects-0.24.0 → metaobjects-0.24.1}/scripts/generate_embedded_library.py +0 -0
  16. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/__init__.py +0 -0
  17. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/agent_context/__init__.py +0 -0
  18. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/agent_context/scaffold.py +0 -0
  19. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/__init__.py +0 -0
  20. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/api_model.py +0 -0
  21. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/builder.py +0 -0
  22. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/naming.py +0 -0
  23. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/paths.py +0 -0
  24. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/apidocs/renderer.py +0 -0
  25. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/attr_class_map.py +0 -0
  26. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/cli.py +0 -0
  27. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  28. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/__init__.py +0 -0
  29. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/collision_names.py +0 -0
  30. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/config.py +0 -0
  31. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/constants.py +0 -0
  32. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  33. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  34. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/format.py +0 -0
  35. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  36. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generator.py +0 -0
  37. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generator_registry.py +0 -0
  38. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/__init__.py +0 -0
  39. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  40. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  41. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  42. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/find_inbound.py +0 -0
  43. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  44. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  45. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  46. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  47. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
  48. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  49. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/router_generator.py +0 -0
  50. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  51. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  52. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  53. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  54. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/metaobjects-config.schema.json +0 -0
  55. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  56. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  57. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/project_config.py +0 -0
  58. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/runner.py +0 -0
  59. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  60. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  61. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  62. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  63. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  64. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  65. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/codegen/type_map.py +0 -0
  66. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/config/__init__.py +0 -0
  67. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/config/neutral_config.py +0 -0
  68. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/config/source_resolver.py +0 -0
  69. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/datatype.py +0 -0
  70. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/documentation/__init__.py +0 -0
  71. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/documentation/doc_constants.py +0 -0
  72. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/documentation/doc_provider.py +0 -0
  73. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/documentation/doc_schema.py +0 -0
  74. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/library/__init__.py +0 -0
  75. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/library/embedded_library.py +0 -0
  76. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/library/library_sources.py +0 -0
  77. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/__init__.py +0 -0
  78. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/merge.py +0 -0
  79. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/meta_data_loader.py +0 -0
  80. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/registered_validation.py +0 -0
  81. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/__init__.py +0 -0
  82. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/directory_source.py +0 -0
  83. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/file_source.py +0 -0
  84. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  85. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/sources/uri_source.py +0 -0
  86. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_discriminator.py +0 -0
  87. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
  88. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_field_mutability.py +0 -0
  89. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  90. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  91. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  92. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/__init__.py +0 -0
  93. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/__init__.py +0 -0
  94. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  95. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  96. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  97. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/field/__init__.py +0 -0
  98. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  99. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  100. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  101. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  102. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  103. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/index/__init__.py +0 -0
  104. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  105. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  106. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/__init__.py +0 -0
  107. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  108. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  109. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  110. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  111. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  112. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/object/value_object.py +0 -0
  113. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  114. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  115. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  116. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  117. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
  118. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/requirement/meta_requirement.py +0 -0
  119. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/requirement/requirement_constants.py +0 -0
  120. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  121. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  122. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/meta_data.py +0 -0
  123. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/meta_root.py +0 -0
  124. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/__init__.py +0 -0
  125. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  126. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  127. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  128. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  129. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  130. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  131. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  132. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  133. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  134. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/__init__.py +0 -0
  135. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  136. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  137. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  138. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  139. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  140. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  141. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  142. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  143. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/provider_extends.py +0 -0
  144. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/template/__init__.py +0 -0
  145. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/template/meta_template.py +0 -0
  146. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  147. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/meta/template/template_constants.py +0 -0
  148. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/naming_refs.py +0 -0
  149. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/parser.py +0 -0
  150. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/parser_yaml.py +0 -0
  151. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/provider.py +0 -0
  152. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/py.typed +0 -0
  153. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/registry.py +0 -0
  154. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/__init__.py +0 -0
  155. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/email_document.py +0 -0
  156. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/escapers.py +0 -0
  157. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  158. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/__init__.py +0 -0
  159. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/coerce.py +0 -0
  160. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/extract.py +0 -0
  161. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/extract_map.py +0 -0
  162. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  163. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/locate.py +0 -0
  164. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/normalize.py +0 -0
  165. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/strip.py +0 -0
  166. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/types.py +0 -0
  167. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  168. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/filesystem_provider.py +0 -0
  169. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/payload_accessors.py +0 -0
  170. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/__init__.py +0 -0
  171. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  172. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  173. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  174. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  175. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  176. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/renderer.py +0 -0
  177. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/render/verify.py +0 -0
  178. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/__init__.py +0 -0
  179. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/llm_recorder.py +0 -0
  180. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  181. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/object_manager.py +0 -0
  182. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/runtime/tph.py +0 -0
  183. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/serializer_json.py +0 -0
  184. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/shared/__init__.py +0 -0
  185. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/shared/base_types.py +0 -0
  186. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/shared/separators.py +0 -0
  187. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/shared/structural.py +0 -0
  188. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/__init__.py +0 -0
  189. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/error_source.py +0 -0
  190. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/json_path.py +0 -0
  191. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/semantic_diff.py +0 -0
  192. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/source/yaml_positions.py +0 -0
  193. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  194. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/attr.json +0 -0
  195. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/db.json +0 -0
  196. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  197. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/field.json +0 -0
  198. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/layout.json +0 -0
  199. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/object.json +0 -0
  200. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  201. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  202. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/requirement.json +0 -0
  203. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/source.json +0 -0
  204. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/template.json +0 -0
  205. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  206. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/ui.json +0 -0
  207. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/validator.json +0 -0
  208. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/spec_metamodel/view.json +0 -0
  209. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/super_resolve.py +0 -0
  210. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/validation_types.py +0 -0
  211. {metaobjects-0.24.0 → metaobjects-0.24.1}/src/metaobjects/yaml_desugar.py +0 -0
  212. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/__init__.py +0 -0
  213. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/__init__.py +0 -0
  214. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  215. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/extends/expected/Program.py +0 -0
  216. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/extends/meta.json +0 -0
  217. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  218. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  219. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/nested-array/meta.json +0 -0
  220. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  221. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  222. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/scalars/meta.json +0 -0
  223. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  224. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/golden/vanilla/meta.json +0 -0
  225. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_abstract_conformance.py +0 -0
  226. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_api_docs_builder.py +0 -0
  227. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_api_docs_paths.py +0 -0
  228. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli.py +0 -0
  229. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_config_gen.py +0 -0
  230. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_config_verify.py +0 -0
  231. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_providers.py +0 -0
  232. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_registry.py +0 -0
  233. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  234. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_verify_strict.py +0 -0
  235. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  236. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_constants_config.py +0 -0
  237. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_entity_model.py +0 -0
  238. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_enum_conformance.py +0 -0
  239. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  240. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_extract_tier_collision.py +0 -0
  241. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_extractor_generator.py +0 -0
  242. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  243. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_format.py +0 -0
  244. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  245. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_fr010_output_codegen.py +0 -0
  246. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  247. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_generator.py +0 -0
  248. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_generator_extension_seams.py +0 -0
  249. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_golden.py +0 -0
  250. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  251. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_inheritance_conformance.py +0 -0
  252. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_instance_artifacts.py +0 -0
  253. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_m2m_codegen.py +0 -0
  254. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_origin_nullability.py +0 -0
  255. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_output_parser_generator.py +0 -0
  256. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_overwrite_policy.py +0 -0
  257. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_overwrite_policy_hash.py +0 -0
  258. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_payload_vo_generator.py +0 -0
  259. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_project_config.py +0 -0
  260. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_projection_compile.py +0 -0
  261. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_render_helper_conformance.py +0 -0
  262. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_render_helper_generator.py +0 -0
  263. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_reverse_finders.py +0 -0
  264. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_router_autoset.py +0 -0
  265. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_router_generator.py +0 -0
  266. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_runner.py +0 -0
  267. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_sourceless_projection.py +0 -0
  268. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_data.py +0 -0
  269. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_generator.py +0 -0
  270. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_output_pattern.py +0 -0
  271. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_scope_helpers.py +0 -0
  272. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_scope_walk.py +0 -0
  273. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_template_spec.py +0 -0
  274. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_tph_codegen.py +0 -0
  275. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_trace_helper_generator.py +0 -0
  276. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_type_map.py +0 -0
  277. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_validation_conformance.py +0 -0
  278. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/codegen/test_write_through.py +0 -0
  279. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/config/__init__.py +0 -0
  280. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/config/test_neutral_config.py +0 -0
  281. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/config/test_source_resolver.py +0 -0
  282. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/__init__.py +0 -0
  283. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/capabilities.py +0 -0
  284. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/conformance-expected-failures.json +0 -0
  285. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/conformance_adapter.py +0 -0
  286. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/corpus.py +0 -0
  287. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/expected_failures.py +0 -0
  288. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/fixture_discovery.py +0 -0
  289. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/navigator.py +0 -0
  290. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  291. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_conformance.py +0 -0
  292. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_extract_conformance.py +0 -0
  293. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_extract_object_verdict.py +0 -0
  294. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  295. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_fr011_attrs.py +0 -0
  296. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_generator_registry_conformance.py +0 -0
  297. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_object_model_conformance.py +0 -0
  298. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_provider_composition_conformance.py +0 -0
  299. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_registry_conformance.py +0 -0
  300. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_runner_hardfail.py +0 -0
  301. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_source_resolution_conformance.py +0 -0
  302. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  303. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_strict_attr_load.py +0 -0
  304. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_template_codegen_conformance.py +0 -0
  305. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_template_generator_conformance.py +0 -0
  306. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/test_yaml_conformance.py +0 -0
  307. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  308. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/__init__.py +0 -0
  309. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/api_contract_assertions.py +0 -0
  310. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/api_contract_jsonb_server.py +0 -0
  311. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/api_contract_m2m_server.py +0 -0
  312. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/api_contract_server.py +0 -0
  313. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/generated_jsonb_app.py +0 -0
  314. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/generated_m2m_app.py +0 -0
  315. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/generated_router_app.py +0 -0
  316. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/generated_tph_app.py +0 -0
  317. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/meta_ai_trace.yaml +0 -0
  318. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/normalization.py +0 -0
  319. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/postgres_container.py +0 -0
  320. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/query_runner.py +0 -0
  321. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/scenarios.py +0 -0
  322. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract.py +0 -0
  323. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_generated.py +0 -0
  324. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_jsonb.py +0 -0
  325. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_m2m.py +0 -0
  326. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  327. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_api_contract_tph_generated.py +0 -0
  328. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  329. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_llm_call_trace.py +0 -0
  330. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_normalization.py +0 -0
  331. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_query_scenarios.py +0 -0
  332. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/integration/test_runtime_return_types.py +0 -0
  333. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/__init__.py +0 -0
  334. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/test_enum_extends_values_conflict.py +0 -0
  335. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/test_field_mutability_order.py +0 -0
  336. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/test_shipped_library_ai.py +0 -0
  337. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/loader/test_template_payload_target_210.py +0 -0
  338. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/open_closed_proof_test.py +0 -0
  339. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/__init__.py +0 -0
  340. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/__init__.py +0 -0
  341. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_coerce.py +0 -0
  342. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_extract.py +0 -0
  343. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_extract_map.py +0 -0
  344. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  345. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_locate.py +0 -0
  346. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_model.py +0 -0
  347. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_normalize.py +0 -0
  348. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_strip.py +0 -0
  349. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  350. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/prompt/__init__.py +0 -0
  351. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/prompt/test_output_format_renderer.py +0 -0
  352. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_email_document.py +0 -0
  353. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_filesystem_provider.py +0 -0
  354. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_output_format_renderer_nested.py +0 -0
  355. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_output_prompt_conformance.py +0 -0
  356. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_render_conformance.py +0 -0
  357. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_render_max_chars.py +0 -0
  358. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_verify.py +0 -0
  359. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/render/test_verify_conformance.py +0 -0
  360. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/runtime/test_object_manager_enum_intvaluemap.py +0 -0
  361. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/__init__.py +0 -0
  362. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_fr5c_merge_attribution.py +0 -0
  363. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_fr5d_reference_resolution.py +0 -0
  364. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_fr5e_database_source_shape.py +0 -0
  365. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_json_path.py +0 -0
  366. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_semantic_diff.py +0 -0
  367. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_source_on_node.py +0 -0
  368. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/source/test_yaml_positions.py +0 -0
  369. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/test_api_docs_accuracy.py +0 -0
  370. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/__init__.py +0 -0
  371. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_agent_context_staleness.py +0 -0
  372. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_capabilities.py +0 -0
  373. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_common_attrs.py +0 -0
  374. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_core_types.py +0 -0
  375. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_effective_package.py +0 -0
  376. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_errors.py +0 -0
  377. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_field_enum.py +0 -0
  378. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_field_enum_intvaluemap.py +0 -0
  379. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_field_map_validation.py +0 -0
  380. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  381. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  382. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_index_lookup.py +0 -0
  383. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_llm_recorder.py +0 -0
  384. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_loader.py +0 -0
  385. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_loader_bom.py +0 -0
  386. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_loader_class.py +0 -0
  387. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_merge.py +0 -0
  388. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_meta_attr.py +0 -0
  389. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_meta_data.py +0 -0
  390. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_meta_source.py +0 -0
  391. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_module_shortcuts.py +0 -0
  392. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_n2m_resolver.py +0 -0
  393. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  394. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  395. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  396. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_object_manager_write_through.py +0 -0
  397. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_one_primary_source.py +0 -0
  398. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_parser.py +0 -0
  399. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_parser_yaml.py +0 -0
  400. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_provider.py +0 -0
  401. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_provider_extension.py +0 -0
  402. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_registry.py +0 -0
  403. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_registry_completeness.py +0 -0
  404. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_registry_extend.py +0 -0
  405. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_registry_sealed.py +0 -0
  406. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_relationship_referential_actions.py +0 -0
  407. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_resolution_key.py +0 -0
  408. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  409. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_serializer.py +0 -0
  410. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_shared_constants.py +0 -0
  411. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_smoke.py +0 -0
  412. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_source_escapes.py +0 -0
  413. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_sources.py +0 -0
  414. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_strict_child_placement.py +0 -0
  415. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_super_resolve.py +0 -0
  416. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_template_toolcall.py +0 -0
  417. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  418. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_attr_schema.py +0 -0
  419. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_filter_values.py +0 -0
  420. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_origin_195.py +0 -0
  421. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_origin_paths.py +0 -0
  422. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_sort_field.py +0 -0
  423. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_validation_warnings.py +0 -0
  424. {metaobjects-0.24.0 → metaobjects-0.24.1}/tests/unit/test_write_coercion_arrays.py +0 -0
  425. {metaobjects-0.24.0 → metaobjects-0.24.1}/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.24.0
3
+ Version: 0.24.1
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.24.0"
3
+ version = "0.24.1"
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"
@@ -541,7 +541,13 @@ core_provider.add(
541
541
  sub_type=IDENTITY_SUBTYPE_SECONDARY,
542
542
  factory=MetaIdentity,
543
543
  attrs=[
544
- AttrSchema(name=IDENTITY_ATTR_FIELDS, value_type=ATTR_SUBTYPE_STRING, required=True, is_array=True),
544
+ # #342: OPTIONAL, unlike identity.primary/reference. The real rule is
545
+ # @fields XOR @expr — a unique index may key off a raw expression
546
+ # instead of columns — and an exclusive-or is not expressible as a
547
+ # per-attr required flag, so it is enforced by the index-key
548
+ # validation pass (ERR_INVALID_INDEX). Required=True here fired first
549
+ # and made an expression index undeclarable.
550
+ AttrSchema(name=IDENTITY_ATTR_FIELDS, value_type=ATTR_SUBTYPE_STRING, required=False, is_array=True),
545
551
  ],
546
552
  child_rules=[ChildRule(TYPE_ATTR, "*")],
547
553
  )
@@ -576,7 +582,9 @@ for _idx_sub in INDEX_SUBTYPES:
576
582
  sub_type=_idx_sub,
577
583
  factory=MetaIndex,
578
584
  attrs=[
579
- AttrSchema(name=INDEX_ATTR_FIELDS, value_type=ATTR_SUBTYPE_STRING, required=True, is_array=True),
585
+ # #342: OPTIONAL — the key is @fields XOR @expr, enforced by the
586
+ # index-key validation pass (ERR_INVALID_INDEX), not by this flag.
587
+ AttrSchema(name=INDEX_ATTR_FIELDS, value_type=ATTR_SUBTYPE_STRING, required=False, is_array=True),
580
588
  ],
581
589
  child_rules=[ChildRule(TYPE_ATTR, "*")],
582
590
  )
@@ -156,6 +156,23 @@ class ErrorCode(str, Enum):
156
156
  # SP-H Unit9 — @filterable: true on a field subtype with no filter-operator
157
157
  # band (e.g. field.object). Would silently generate an empty-ops filter.
158
158
  ERR_FILTERABLE_UNSUPPORTED_SUBTYPE = "ERR_FILTERABLE_UNSUPPORTED_SUBTYPE"
159
+ # #335 Half B — @sortable: true on an array field or a subtype with no
160
+ # filter-operator band (e.g. field.object). Would silently emit a sort
161
+ # entry over a column no dialect can ORDER BY.
162
+ ERR_SORTABLE_UNSUPPORTED_SUBTYPE = "ERR_SORTABLE_UNSUPPORTED_SUBTYPE"
163
+ # #335 Half A — a whole-object @agg:collect (no @of; the carrying field.object
164
+ # rolls related rows up as its declared @objectRef value object) is malformed:
165
+ # carrier is not a field.object with @objectRef, @via absent, @distinct declared
166
+ # (refused — a no-op whenever the value object carries the primary key), an
167
+ # @orderBy key not on the @via TERMINAL entity, or a member's declared type
168
+ # disagreeing with the matched terminal field's. Distinct from ERR_INVALID_ORIGIN
169
+ # so a fixture can tell this arm from a loader that still requires @of.
170
+ ERR_COLLECT_WHOLE_OBJECT = "ERR_COLLECT_WHOLE_OBJECT"
171
+ # #335 Half A — a whole-object @agg:collect's value-object member has no
172
+ # matching field (by name) on the @via terminal entity. The lowering
173
+ # projects exactly the declared members; failing open here is how #270
174
+ # turned a curated value object into the full entity.
175
+ ERR_COLLECT_MEMBER_UNRESOLVED = "ERR_COLLECT_MEMBER_UNRESOLVED"
159
176
  # ADR-0023 — a registration was attempted against a registry sealed after its
160
177
  # agreed metamodel-provider bootstrap. Codegen cannot invent metamodel attrs.
161
178
  ERR_REGISTRY_SEALED = "ERR_REGISTRY_SEALED"
@@ -26,6 +26,7 @@ from ..meta.core.field.field_constants import (
26
26
  FIELD_ATTR_INT_VALUE_MAP,
27
27
  FIELD_ATTR_OBJECT_REF,
28
28
  FIELD_ATTR_REQUIRED,
29
+ FIELD_ATTR_SORTABLE,
29
30
  FIELD_ATTR_STORAGE,
30
31
  FIELD_ATTR_VALUE_TYPE,
31
32
  FIELD_ATTR_VALUES,
@@ -124,8 +125,14 @@ from ..meta.core.object.object_constants import (
124
125
  OBJECT_SUBTYPE_PROJECTION,
125
126
  OBJECT_SUBTYPE_VALUE,
126
127
  )
127
- from ..meta.core.identity.identity_constants import IDENTITY_ATTR_FIELDS
128
+ from ..meta.core.identity.identity_constants import (
129
+ IDENTITY_ATTR_FIELDS,
130
+ IDENTITY_SUBTYPE_SECONDARY,
131
+ )
128
132
  from ..meta.core.index.index_constants import INDEX_ATTR_FIELDS, INDEX_SUBTYPE_LOOKUP
133
+ from ..meta.persistence.db.db_constants import (
134
+ IDENTITY_SECONDARY_ATTR_EXPR as IDENTITY_ATTR_EXPR,
135
+ )
129
136
  from ..source import resolved_source
130
137
  from ..naming_refs import did_you_mean_hint, resolve_object_ref
131
138
 
@@ -224,8 +231,10 @@ def run_validations(
224
231
  _validate_identity_passthrough(root, errors)
225
232
  _validate_max_occurs(root, registry, errors)
226
233
  _validate_filterable_has_index(root, warnings)
227
- # SP-H Unit9 — @filterable on a subtype with no operator band → error.
234
+ # SP-H Unit9 — @filterable on a subtype with no operator band, or an array → error.
228
235
  _validate_filterable_has_supported_ops(root, errors)
236
+ # #335 Half B — @sortable on an array field, or a subtype with no operator band → error.
237
+ _validate_sortable_has_supported_subtype(root, errors)
229
238
  _validate_index_lookup_fields(root, errors)
230
239
 
231
240
 
@@ -1575,6 +1584,16 @@ def _validate_entity_field_ref(
1575
1584
  return (entity, field_node)
1576
1585
 
1577
1586
 
1587
+ class WalkedViaPath(NamedTuple):
1588
+ """A fully-walked ``@via`` path: the relationship hop nodes in path order,
1589
+ plus the entity they terminate at. Returned as a pair (rather than re-walking
1590
+ for the terminal) because a second walk means a second copy of the ADR-0042
1591
+ package-resolution rule — mirrors the TS ``WalkedViaPath``."""
1592
+
1593
+ hops: list[MetaData]
1594
+ terminal: MetaData
1595
+
1596
+
1578
1597
  def _validate_via_path(
1579
1598
  via: str,
1580
1599
  context: str,
@@ -1583,12 +1602,15 @@ def _validate_via_path(
1583
1602
  errors: list[MetaError],
1584
1603
  origin_node: MetaData,
1585
1604
  referrer: str,
1586
- ) -> list[MetaData] | None:
1605
+ ) -> WalkedViaPath | None:
1587
1606
  """Validate a dotted relationship path 'Entity.rel1[.rel2...]'.
1588
1607
 
1589
- Returns the walked relationship hop nodes (in path order) on full success
1590
- (FR-024 B5 runs the cardinality checks over them); appends ERR_INVALID_ORIGIN
1591
- and returns None if not.
1608
+ Returns the walked relationship hop nodes (in path order) together with the
1609
+ TERMINAL entity node on full success (FR-024 B5 runs the cardinality checks
1610
+ over the hops; #335's whole-object @agg:collect has no @of entity, so its
1611
+ @orderBy keys and value-object members resolve against the terminal);
1612
+ appends ERR_INVALID_ORIGIN and returns None if not. ``terminal`` is defined
1613
+ exactly when ``hops`` is — every early exit returns None.
1592
1614
 
1593
1615
  *origin_node* carries the parse-time envelope (files/json_path); *referrer*
1594
1616
  is the canonical referrer FQN (``<projection-FQN>::<fieldName>``) attached
@@ -1686,7 +1708,8 @@ def _validate_via_path(
1686
1708
  hops.append(rel_node)
1687
1709
  current_entity = next_entity
1688
1710
 
1689
- return hops
1711
+ # current_entity is the terminal: every earlier exit returned None.
1712
+ return WalkedViaPath(hops, current_entity)
1690
1713
 
1691
1714
 
1692
1715
  # ---------------------------------------------------------------------------
@@ -1995,6 +2018,61 @@ def _check_passthrough_type(
1995
2018
  )
1996
2019
 
1997
2020
 
2021
+ def _type_label(field: MetaData) -> str:
2022
+ """A field's declared type on BOTH axes — ``field.<subType>`` plus ``[]`` when it is
2023
+ an array. ADR-0039: resolved_is_array(), so array-ness inherited via extends counts.
2024
+ Mirrors the TS ``_typeLabel``."""
2025
+ return f"field.{field.sub_type}{'[]' if field.resolved_is_array() else ''}"
2026
+
2027
+
2028
+ def _check_collect_members(
2029
+ ref_target: MetaData,
2030
+ terminal: MetaData,
2031
+ obj: MetaData,
2032
+ field: MetaData,
2033
+ src: object,
2034
+ errors: list[MetaError],
2035
+ ) -> None:
2036
+ """#335 — a whole-object @agg:collect projects EXACTLY the declared value object's
2037
+ members, matched BY NAME against the @via terminal entity's fields:
2038
+
2039
+ - an unmatched member is an error, never a silent drop. Failing open here is how
2040
+ #270 turned a curated value object into the full entity, invisible in a diff
2041
+ because the metadata still read as curated.
2042
+ - a matched member must agree on BOTH type axes (#185 type-preserving doctrine),
2043
+ so a scalar member cannot bind an array field or vice versa.
2044
+
2045
+ Both refusals carry a whole-object-specific code — ERR_COLLECT_MEMBER_UNRESOLVED for
2046
+ the unmatched member, ERR_COLLECT_WHOLE_OBJECT for the type disagreement. The latter
2047
+ is deliberately NOT the scalar arm's ERR_INVALID_ORIGIN: a loader that still requires
2048
+ @of rejects this metadata with ERR_INVALID_ORIGIN too, so sharing the code would make
2049
+ a corpus fixture pass on a port that implements nothing.
2050
+
2051
+ Mirrors the TS ``_checkCollectMembers``."""
2052
+ # ADR-0039: resolving — a value object may inherit members via extends, and the
2053
+ # terminal entity may inherit fields; own-only would silently skip inherited
2054
+ # members, which is exactly the #270 bug class this guards.
2055
+ terminal_fields = [c for c in terminal.children() if c.type == TYPE_FIELD]
2056
+ for member in (c for c in ref_target.children() if c.type == TYPE_FIELD):
2057
+ match = next((f for f in terminal_fields if f.name == member.name), None)
2058
+ if match is None:
2059
+ errors.append(MetaError(
2060
+ f"origin.aggregate @agg:collect on {obj.name}.{field.name}: value-object "
2061
+ f"member '{member.name}' has no matching field on '{terminal.name}' — a "
2062
+ f"whole-object rollup projects exactly the declared members.",
2063
+ ErrorCode.ERR_COLLECT_MEMBER_UNRESOLVED, envelope=src))
2064
+ continue
2065
+ member_label = _type_label(member)
2066
+ match_label = _type_label(match)
2067
+ if member_label != match_label:
2068
+ errors.append(MetaError(
2069
+ f"origin.aggregate @agg:collect on {obj.name}.{field.name}: value-object "
2070
+ f"member '{member.name}' is {member_label} but "
2071
+ f"'{terminal.name}.{match.name}' is {match_label} — a whole-object rollup "
2072
+ f"preserves each member's type.",
2073
+ ErrorCode.ERR_COLLECT_WHOLE_OBJECT, envelope=src))
2074
+
2075
+
1998
2076
  def _validate_order_by_keys(
1999
2077
  order_by: object,
2000
2078
  related_entity: MetaData | None,
@@ -2003,12 +2081,17 @@ def _validate_order_by_keys(
2003
2081
  label: str,
2004
2082
  origin_source: object,
2005
2083
  errors: list[MetaError],
2084
+ code: ErrorCode = ErrorCode.ERR_INVALID_ORIGIN,
2006
2085
  ) -> None:
2007
2086
  """#195 — validate that ``@orderBy`` keys ('field[:asc|desc]') resolve against the
2008
2087
  RELATED entity's effective fields (the entity reached via @via/@of), and that any
2009
2088
  direction suffix is asc/desc. Shared by @agg:collect (element order) and origin.first
2010
2089
  (row selection). A missing related entity means a prior error already fired — skip.
2011
- Mirrors the TS _validateOrderByKeys."""
2090
+ Mirrors the TS _validateOrderByKeys.
2091
+
2092
+ *code* lets #335's whole-object @agg:collect arm report ERR_COLLECT_WHOLE_OBJECT
2093
+ instead; it defaults to ERR_INVALID_ORIGIN so the scalar @of and origin.first call
2094
+ sites keep their existing envelope byte-for-byte."""
2012
2095
  if not isinstance(order_by, (list, tuple)) or related_entity is None:
2013
2096
  return
2014
2097
  for raw in order_by:
@@ -2027,7 +2110,7 @@ def _validate_order_by_keys(
2027
2110
  MetaError(
2028
2111
  f'{label} on {obj.name}.{field_name}: @orderBy key "{raw}" — no such '
2029
2112
  f'field "{key}" on {related_entity.name}.',
2030
- ErrorCode.ERR_INVALID_ORIGIN,
2113
+ code,
2031
2114
  envelope=origin_source,
2032
2115
  )
2033
2116
  )
@@ -2036,7 +2119,7 @@ def _validate_order_by_keys(
2036
2119
  MetaError(
2037
2120
  f'{label} on {obj.name}.{field_name}: @orderBy key "{raw}" — direction '
2038
2121
  f"must be one of {'|'.join(SORT_ORDER_VALUES)}.",
2039
- ErrorCode.ERR_INVALID_ORIGIN,
2122
+ code,
2040
2123
  envelope=origin_source,
2041
2124
  )
2042
2125
  )
@@ -2130,7 +2213,8 @@ def _validate_origin_paths(
2130
2213
  )
2131
2214
  via = origin.attr(ORIGIN_ATTR_VIA)
2132
2215
  if isinstance(via, str) and via:
2133
- hops = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
2216
+ walked = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
2217
+ hops = walked.hops if walked is not None else None
2134
2218
  if hops is not None:
2135
2219
  _check_passthrough_cardinality(hops, node.name, origin.source, errors)
2136
2220
  elif from_target is not None and not is_value_host:
@@ -2226,20 +2310,94 @@ def _validate_origin_paths(
2226
2310
  f"explicit @via (a quantifier has no @of to infer the path from).",
2227
2311
  ErrorCode.ERR_INVALID_ORIGIN, envelope=src))
2228
2312
  else:
2229
- hops = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
2313
+ walked = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
2314
+ hops = walked.hops if walked is not None else None
2230
2315
  if hops is not None:
2231
2316
  _check_aggregate_cardinality(hops, node.name, src, errors)
2232
2317
  continue
2233
2318
 
2234
- # --- count/sum/avg/min/max/collect: @of REQUIRED ---
2319
+ # --- @of: REQUIRED for count/sum/avg/min/max; OPTIONAL for collect ---
2320
+ # #335 — an @of-absent collect is a WHOLE-OBJECT rollup: collect the
2321
+ # related rows as an array of the field's declared @objectRef value
2322
+ # object rather than an array of one scalar column.
2235
2323
  if not of_present:
2236
- errors.append(
2237
- MetaError(
2238
- f"{ctx} is missing required attribute '@{ORIGIN_ATTR_OF}'",
2239
- ErrorCode.ERR_INVALID_ORIGIN,
2240
- envelope=src,
2324
+ if not is_collect:
2325
+ errors.append(
2326
+ MetaError(
2327
+ f"{ctx} is missing required attribute '@{ORIGIN_ATTR_OF}'",
2328
+ ErrorCode.ERR_INVALID_ORIGIN,
2329
+ envelope=src,
2330
+ )
2241
2331
  )
2242
- )
2332
+ continue
2333
+ # Whole-object rollup. The carrying field must be a field.object
2334
+ # naming a value object, and @via must be explicit (there is no @of
2335
+ # entity to infer the single-hop relation from).
2336
+ # ADR-0039: resolving — @objectRef may be inherited via extends.
2337
+ object_ref = node.get_meta_attr(FIELD_ATTR_OBJECT_REF)
2338
+ if node.sub_type != FIELD_SUBTYPE_OBJECT or not isinstance(object_ref, str) \
2339
+ or not object_ref:
2340
+ errors.append(MetaError(
2341
+ f"origin.aggregate @agg:collect on {obj.name}.{node.name}: @of is "
2342
+ f"omitted, so this is a whole-object rollup — the carrying field "
2343
+ f"must be a field.object declaring @objectRef (add @of to collect a "
2344
+ f"single column instead).",
2345
+ ErrorCode.ERR_COLLECT_WHOLE_OBJECT, envelope=src))
2346
+ continue
2347
+ # #210's value-only rule is PAYLOAD-scoped and never reaches a
2348
+ # projection-hosted field, so this branch enforces it itself.
2349
+ # Without it an @objectRef to an entity silently rolls up the FULL
2350
+ # entity — the #270 shape, this time baked into DDL.
2351
+ # ADR-0042 — a bare @objectRef resolves in the DECLARING owner's
2352
+ # package (an inherited field resolves in the package that declared it).
2353
+ ref_owner = node.parent or obj
2354
+ ref_pkg = ref_owner.package or ref_owner.file_default_package or ""
2355
+ ref_target = resolve_object_ref(root, object_ref, ref_pkg)
2356
+ if ref_target is not None and ref_target.sub_type != OBJECT_SUBTYPE_VALUE:
2357
+ errors.append(MetaError(
2358
+ f"origin.aggregate @agg:collect on {obj.name}.{node.name}: "
2359
+ f"@objectRef '{object_ref}' resolves to "
2360
+ f"{TYPE_OBJECT}.{ref_target.sub_type} — a whole-object rollup must "
2361
+ f"target an object.value (#210, ADR-0028).",
2362
+ ErrorCode.ERR_SUBTYPE_RULE_VIOLATION, envelope=src))
2363
+ continue
2364
+ # ADR-0039: own — origin.* never inherits (ADR-0029).
2365
+ via_attr = origin.attr(ORIGIN_ATTR_VIA)
2366
+ if not isinstance(via_attr, str) or not via_attr:
2367
+ errors.append(MetaError(
2368
+ f"origin.aggregate @agg:collect on {obj.name}.{node.name}: @via is "
2369
+ f"required on a whole-object rollup — there is no @of entity to "
2370
+ f"infer the relationship from.",
2371
+ ErrorCode.ERR_COLLECT_WHOLE_OBJECT, envelope=src))
2372
+ continue
2373
+ # @distinct is refused on the object form. It is NOT an engine limit
2374
+ # (both engines dedupe JSON objects); it is a guaranteed no-op
2375
+ # whenever the value object carries the entity's primary key, which
2376
+ # is the common case, and a silent no-op is worse than a refusal.
2377
+ if has_distinct:
2378
+ errors.append(MetaError(
2379
+ f"origin.aggregate @agg:collect on {obj.name}.{node.name}: @distinct "
2380
+ f"is not supported on a whole-object rollup (it is a no-op whenever "
2381
+ f"the value object carries the primary key).",
2382
+ ErrorCode.ERR_COLLECT_WHOLE_OBJECT, envelope=src))
2383
+ continue
2384
+ # One walk yields both the hops (cardinality) and the terminal entity
2385
+ # (@orderBy keys, member resolution). An invalid @via (e.g. a single-
2386
+ # segment "A") returns None having already pushed its own error, so
2387
+ # everything downstream is skipped and no second, misleadingly-scoped
2388
+ # error is emitted.
2389
+ walked = _validate_via_path(
2390
+ via_attr, ctx, root, host_pkg, errors, origin, referrer)
2391
+ if walked is not None:
2392
+ _check_aggregate_cardinality(walked.hops, node.name, src, errors)
2393
+ # @orderBy keys resolve against the @via TERMINAL entity, not @of.
2394
+ _validate_order_by_keys(
2395
+ order_by, walked.terminal, obj, node.name,
2396
+ "origin.aggregate @agg:collect", src, errors,
2397
+ ErrorCode.ERR_COLLECT_WHOLE_OBJECT)
2398
+ if ref_target is not None:
2399
+ _check_collect_members(
2400
+ ref_target, walked.terminal, obj, node, src, errors)
2243
2401
  continue
2244
2402
  # NOTE (FR-024 B6): NO extends/origin agreement on aggregates —
2245
2403
  # an aggregate computes something new (spec §4 is passthrough-only).
@@ -2261,7 +2419,8 @@ def _validate_origin_paths(
2261
2419
  obj, node.name, "origin.aggregate @agg:collect", src, errors)
2262
2420
  via = origin.attr(ORIGIN_ATTR_VIA)
2263
2421
  if isinstance(via, str) and via:
2264
- hops = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
2422
+ walked = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
2423
+ hops = walked.hops if walked is not None else None
2265
2424
  if hops is not None:
2266
2425
  _check_aggregate_cardinality(hops, node.name, src, errors)
2267
2426
  continue
@@ -2372,7 +2531,8 @@ def _validate_origin_paths(
2372
2531
  # @via — explicit (validated + cardinality) or single-hop-unique inferred.
2373
2532
  via = origin.attr(ORIGIN_ATTR_VIA)
2374
2533
  if isinstance(via, str) and via:
2375
- hops = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
2534
+ walked = _validate_via_path(via, ctx, root, host_pkg, errors, origin, referrer)
2535
+ hops = walked.hops if walked is not None else None
2376
2536
  if hops is not None:
2377
2537
  _check_aggregate_cardinality(hops, node.name, src, errors)
2378
2538
  elif of_target is not None:
@@ -3123,6 +3283,24 @@ def _validate_filterable_has_supported_ops(
3123
3283
  for field in node.fields():
3124
3284
  if field.attrs().get("filterable") is not True:
3125
3285
  continue
3286
+
3287
+ # #335 Half B — an ARRAY field has no operator band either. Every
3288
+ # FR-009 operator (eq/ne/gt/gte/lt/lte/in/like/isNull) is a scalar
3289
+ # comparison; none applies to a collection column. Same reason as
3290
+ # the subtype check below, so same code.
3291
+ # ADR-0039: resolved_is_array(), never the own `is_array` flag.
3292
+ if field.resolved_is_array():
3293
+ errors.append(
3294
+ MetaError(
3295
+ f'Field "{node.name}.{field.name}" has @filterable: true but is an array '
3296
+ f"(isArray: true). No filter operator applies to a collection column. "
3297
+ f"Remove @filterable from this field.",
3298
+ ErrorCode.ERR_FILTERABLE_UNSUPPORTED_SUBTYPE,
3299
+ envelope=field.source,
3300
+ )
3301
+ )
3302
+ continue
3303
+
3126
3304
  if ops_for_subtype(field.sub_type):
3127
3305
  continue
3128
3306
  errors.append(
@@ -3137,6 +3315,46 @@ def _validate_filterable_has_supported_ops(
3137
3315
  )
3138
3316
 
3139
3317
 
3318
+ # ---------------------------------------------------------------------------
3319
+ # Pass: @sortable on an array field or unsupported subtype (#335 Half B)
3320
+ # ---------------------------------------------------------------------------
3321
+ # @sortable defaults FROM @filterable, so it is checked independently only
3322
+ # when explicit — nothing validated it before, while @filterable has had a
3323
+ # hard error since SP-H Unit9. A @sortable JSON or array column emits a sort
3324
+ # entry over a column no dialect can ORDER BY meaningfully.
3325
+ # → ERR_SORTABLE_UNSUPPORTED_SUBTYPE.
3326
+
3327
+
3328
+ def _validate_sortable_has_supported_subtype(
3329
+ root: MetaData,
3330
+ errors: list[MetaError],
3331
+ ) -> None:
3332
+ for node in _walk(root):
3333
+ if node.type != TYPE_OBJECT or not isinstance(node, MetaObject):
3334
+ continue
3335
+ for field in node.fields():
3336
+ if field.attrs().get(FIELD_ATTR_SORTABLE) is not True:
3337
+ continue
3338
+ # ADR-0039: resolved_is_array(), never the own `is_array` flag.
3339
+ is_array = field.resolved_is_array()
3340
+ if not is_array and ops_for_subtype(field.sub_type):
3341
+ continue
3342
+
3343
+ reason = (
3344
+ "is an array (isArray: true) — a collection column has no ordering."
3345
+ if is_array
3346
+ else f'its subtype "{field.sub_type}" cannot be ordered.'
3347
+ )
3348
+ errors.append(
3349
+ MetaError(
3350
+ f'Field "{node.name}.{field.name}" has @sortable: true but {reason} '
3351
+ f"Remove @sortable from this field.",
3352
+ ErrorCode.ERR_SORTABLE_UNSUPPORTED_SUBTYPE,
3353
+ envelope=field.source,
3354
+ )
3355
+ )
3356
+
3357
+
3140
3358
  # ---------------------------------------------------------------------------
3141
3359
  # Pass: field.object @storage validation
3142
3360
  # ---------------------------------------------------------------------------
@@ -3509,12 +3727,22 @@ def _parse_string_list(raw: object) -> tuple[str, ...]:
3509
3727
 
3510
3728
 
3511
3729
  # ---------------------------------------------------------------------------
3512
- # Pass: index.lookup @fields resolution
3730
+ # Pass: index-key resolution for index.lookup AND identity.secondary (#342)
3731
+ #
3732
+ # The key is @fields XOR @expr. @expr has always been registered as "used
3733
+ # INSTEAD of @fields" and migrate-ts has always keyed off it
3734
+ # (`columns: expr ? [] : cols`), but the loader required @fields
3735
+ # unconditionally — so an expression index could not be declared, and the one
3736
+ # spelling that DID load (@fields AND @expr) had its @fields silently discarded
3737
+ # downstream. Declaring both is now refused rather than half-honored, which is
3738
+ # the silent-wrong-output the sealed strict registry exists to prevent.
3739
+ #
3740
+ # identity.secondary is included because per ADR-0040 uniqueness lives in the
3741
+ # TYPE — identity.secondary IS a unique index and keys itself identically,
3742
+ # carrying @expr from the same db provider.
3513
3743
  #
3514
- # Every index.lookup on an entity must name at least one field, and every
3515
- # named field must exist in the entity's EFFECTIVE (resolved) field set.
3516
- # ADR-0039: use children() / MetaIndex.fields() — never own* — so that a
3517
- # field inherited via extends still resolves correctly.
3744
+ # ADR-0039: children() / MetaIndex.fields() — never own* — so a field inherited
3745
+ # via extends still resolves correctly.
3518
3746
  # ---------------------------------------------------------------------------
3519
3747
 
3520
3748
  def _validate_index_lookup_fields(root: MetaData, errors: list[MetaError]) -> None:
@@ -3525,30 +3753,77 @@ def _validate_index_lookup_fields(root: MetaData, errors: list[MetaError]) -> No
3525
3753
  f.name for f in obj.children() if f.type == TYPE_FIELD
3526
3754
  }
3527
3755
  for node in obj.children():
3528
- if node.type != TYPE_INDEX or node.sub_type != INDEX_SUBTYPE_LOOKUP:
3756
+ is_lookup = node.type == TYPE_INDEX and node.sub_type == INDEX_SUBTYPE_LOOKUP
3757
+ is_secondary = (
3758
+ node.type == TYPE_IDENTITY
3759
+ and node.sub_type == IDENTITY_SUBTYPE_SECONDARY
3760
+ )
3761
+ if not (is_lookup or is_secondary):
3529
3762
  continue
3530
- if not isinstance(node, MetaIndex):
3763
+ label = f"{node.type}.{node.sub_type}"
3764
+ # PRESENCE vs CONTENT are two different questions, and conflating them is
3765
+ # a bug in both directions. The CONTRADICTION check needs PRESENCE — an
3766
+ # explicit `@fields: []` beside @expr is still a declaration of both, and
3767
+ # keying it on non-emptiness let that spelling load clean while
3768
+ # `@fields: ["x"]` + @expr was refused. The KEY-RESOLUTION check needs
3769
+ # normalized CONTENT, which folds absent/scalar/empty together — the
3770
+ # normalization is the fix for one and the obstacle for the other, so the
3771
+ # two questions are asked separately. attrs() is the RESOLVING accessor in
3772
+ # Python (attr() is own-only — ADR-0039).
3773
+ attrs = node.attrs()
3774
+ has_fields_attr = IDENTITY_ATTR_FIELDS in attrs
3775
+ if isinstance(node, MetaIndex):
3776
+ fields = node.fields()
3777
+ else:
3778
+ raw = attrs.get(IDENTITY_ATTR_FIELDS)
3779
+ fields = list(raw) if isinstance(raw, (list, tuple)) else []
3780
+ expr = attrs.get(IDENTITY_ATTR_EXPR)
3781
+ has_expr = isinstance(expr, str) and expr.strip() != ""
3782
+
3783
+ # Rule 1a: exactly one of @fields / @expr may be DECLARED.
3784
+ if has_fields_attr and has_expr:
3785
+ errors.append(MetaError(
3786
+ code=ErrorCode.ERR_INVALID_INDEX,
3787
+ message=(
3788
+ f'{label} "{node.name}" on "{obj.name}" declares BOTH '
3789
+ f"@{INDEX_ATTR_FIELDS} and @{IDENTITY_ATTR_EXPR}; they are the "
3790
+ f"two mutually exclusive ways to key an index. "
3791
+ f"@{IDENTITY_ATTR_EXPR} is used INSTEAD of @{INDEX_ATTR_FIELDS} "
3792
+ f"— drop one. (Declaring both previously loaded but silently "
3793
+ f"discarded @{INDEX_ATTR_FIELDS}.)"
3794
+ ),
3795
+ envelope=node.source,
3796
+ ))
3531
3797
  continue
3532
- fields = node.fields()
3533
3798
 
3534
- if len(fields) == 0:
3799
+ # Rule 1b: whichever is declared must actually supply a key.
3800
+ # envelope=node.source — FR5a/ADR-0009 provenance. TS, Java and C# have
3801
+ # always attached it here; Python did not, so these errors arrived with an
3802
+ # empty envelope. The cross-port corpus asserts envelope SHAPE, which is
3803
+ # what surfaced it.
3804
+ if len(fields) == 0 and not has_expr:
3535
3805
  errors.append(MetaError(
3536
3806
  code=ErrorCode.ERR_INVALID_INDEX,
3537
3807
  message=(
3538
- f'index.lookup "{node.name}" on "{obj.name}" has no '
3539
- f"@{INDEX_ATTR_FIELDS}; at least one field is required"
3808
+ f'{label} "{node.name}" on "{obj.name}" declares no key: '
3809
+ f"it must have @{INDEX_ATTR_FIELDS} (one or more columns) "
3810
+ f"or @{IDENTITY_ATTR_EXPR} (a key expression)"
3540
3811
  ),
3812
+ envelope=node.source,
3541
3813
  ))
3542
3814
  continue
3543
3815
 
3816
+ # Rule 2: every named field must resolve. An @expr index has no
3817
+ # @fields to resolve — @expr is raw SQL, deliberately not parsed here.
3544
3818
  for field_name in fields:
3545
3819
  if field_name not in effective_field_names:
3546
3820
  errors.append(MetaError(
3547
3821
  code=ErrorCode.ERR_INVALID_INDEX,
3548
3822
  message=(
3549
- f'index.lookup "{node.name}" on "{obj.name}" references '
3823
+ f'{label} "{node.name}" on "{obj.name}" references '
3550
3824
  f'field "{field_name}" which does not exist on "{obj.name}". '
3551
3825
  f"Available fields: "
3552
3826
  f"{', '.join(sorted(effective_field_names)) or '(none)'}"
3553
3827
  ),
3828
+ envelope=node.source,
3554
3829
  ))
@@ -255,7 +255,7 @@ def _sorted_children(definition: TypeDefinition) -> list[dict[str, object]]:
255
255
  return children
256
256
 
257
257
 
258
- METAMODEL_VERSION = "0.10"
258
+ METAMODEL_VERSION = "0.12"
259
259
  """Rolled-up spec-version for the cross-port registry manifest.
260
260
 
261
261
  ``"0"`` = pre-1.0 / unstable (semver major-0). Flips to ``"1.0"`` at the
@@ -19,7 +19,7 @@
19
19
  "description": "A secondary unique index on one or more fields. Always enforces uniqueness — use index.lookup for non-unique query-performance indexes.",
20
20
  "whenToUse": "A column or set of columns must be unique (e.g. email, slug, composite business key). For a plain non-unique index, use index.lookup instead.",
21
21
  "children": [
22
- { "type": "attr", "subType": "string", "name": "fields", "isArray": true, "min": 1, "max": 1, "description": "The field name(s) composing this identity. Single-element for a simple unique index, multiple for a composite unique constraint." }
22
+ { "type": "attr", "subType": "string", "name": "fields", "isArray": true, "min": 0, "max": 1, "description": "The field name(s) composing this identity. Single-element for a simple unique index, multiple for a composite unique constraint. Required UNLESS @expr is present — a unique index keys off plain columns (@fields) or a key expression (@expr), never both; declaring both is ERR_INVALID_INDEX." }
23
23
  ]
24
24
  },
25
25
  {
@@ -7,7 +7,7 @@
7
7
  "description": "A non-unique lookup index on one or more fields. Use for query-performance indexes that do NOT enforce uniqueness — declare identity.secondary for unique constraints instead.",
8
8
  "whenToUse": "You need a DB index for query performance (fast lookups/sorts) but NOT uniqueness enforcement. @fields names the indexed columns; the db provider adds @orders / @expr / @where / @using for physical tuning.",
9
9
  "children": [
10
- { "type": "attr", "subType": "string", "name": "fields", "isArray": true, "min": 1, "max": 1, "description": "The field name(s) this index covers (at least one). When @expr is present, it is the key expression derived from these fields." }
10
+ { "type": "attr", "subType": "string", "name": "fields", "isArray": true, "min": 0, "max": 1, "description": "The field name(s) this index covers. Required UNLESS @expr is present — an index keys off plain columns (@fields) or a key expression (@expr), never both; declaring both is ERR_INVALID_INDEX." }
11
11
  ]
12
12
  }
13
13
  ]
@@ -20,15 +20,15 @@
20
20
  {
21
21
  "type": "origin",
22
22
  "subType": "aggregate",
23
- "description": "A value reduced from the related row-set reached along a relationship path (@via) from the base entity: count/sum/avg/min/max over a column (@of); any/all predicate quantifiers over a @filter; or collect (an array rollup of @of).",
23
+ "description": "A value reduced from the related row-set reached along a relationship path (@via) from the base entity: count/sum/avg/min/max over a column (@of); any/all predicate quantifiers over a @filter; or collect (an array rollup — of the @of column, or of the carrying field.object's declared @objectRef value object when @of is omitted).",
24
24
  "whenToUse": "A projection needs a value derived by reducing related rows — a count/sum/avg/min/max, a 'did any/every related row match' flag, or an array of collected values. Declare it instead of hand-writing the aggregate query — it stays consistent and regenerates.",
25
- "rules": "@via may be omitted only when exactly one single-hop relationship leads from the base entity to the @of entity (single-hop-unique inference; FR-024, ADR-0029). Multi-hop paths must always be stated explicitly. @of is required for count/sum/avg/min/max/collect and forbidden for any/all (which quantify over rows via @filter, not a column). @filter is required for any/all. The field must be isArray:true for collect and isArray:false for every other @agg. @distinct and @orderBy are collect-only.",
25
+ "rules": "@via may be omitted only when exactly one single-hop relationship leads from the base entity to the @of entity (single-hop-unique inference; FR-024, ADR-0029). Multi-hop paths must always be stated explicitly. @of is required for count/sum/avg/min/max and forbidden for any/all (which quantify over rows via @filter, not a column). @filter is required for any/all. The field must be isArray:true for collect and isArray:false for every other @agg. @distinct and @orderBy are collect-only. On @agg:collect @of is OPTIONAL: omitting it declares a WHOLE-OBJECT rollup, which collects each related row as the carrying field's declared @objectRef value object instead of one scalar column. A whole-object rollup requires a field.object carrying @objectRef, requires that @objectRef to name an object.value, requires an explicit @via (there is no @of entity to infer the path from), and refuses @distinct. Its @orderBy keys resolve against the @via TERMINAL entity, not the head or a middle hop. Its value-object members bind to the terminal entity's fields BY NAME — member name == terminal field name, deliberately NOT extends, so one value object stays collectable from two different entities — and every member must match a terminal field agreeing on BOTH field.<subType> and array-ness.",
26
26
  "children": [
27
- { "type": "attr", "subType": "string", "name": "agg", "min": 1, "max": 1, "allowedValues": ["count", "sum", "avg", "min", "max", "any", "all", "collect"], "description": "The reducing function applied over the related row-set: count/sum/avg/min/max (numeric/ordinal reduces over @of); any/all (predicate quantifiers over @filter — @of forbidden; empty set → any=false, all=true); collect (array rollup of @of — the field must be isArray)." },
28
- { "type": "attr", "subType": "string", "name": "of", "min": 0, "max": 1, "description": "Dotted Entity.field reference identifying the column being aggregated (e.g. 'Week.durationMinutes'). Required for count/sum/avg/min/max/collect; forbidden for any/all (which quantify over rows via @filter, not a column)." },
27
+ { "type": "attr", "subType": "string", "name": "agg", "min": 1, "max": 1, "allowedValues": ["count", "sum", "avg", "min", "max", "any", "all", "collect"], "description": "The reducing function applied over the related row-set: count/sum/avg/min/max (numeric/ordinal reduces over @of); any/all (predicate quantifiers over @filter — @of forbidden; empty set → any=false, all=true); collect (array rollup — of the @of column, or of the carrying field.object's declared @objectRef value object when @of is omitted; the field must be isArray)." },
28
+ { "type": "attr", "subType": "string", "name": "of", "min": 0, "max": 1, "description": "Dotted Entity.field reference identifying the column being aggregated (e.g. 'Week.durationMinutes'). Required for count/sum/avg/min/max; OPTIONAL for collect, where absent means a whole-object rollup of the field's declared @objectRef value object; forbidden for any/all (which quantify over rows via @filter, not a column)." },
29
29
  { "type": "attr", "subType": "string", "name": "via", "min": 0, "max": 1, "description": "Dotted relationship path from the base entity to the aggregated rows (e.g. 'Program.weeks' or 'Program.weeks.workouts'). May be omitted only when exactly one single-hop relationship leads from the base entity to the @of entity (FR-024, ADR-0029)." },
30
30
  { "type": "attr", "subType": "filter", "name": "filter", "min": 0, "max": 1, "description": "Optional structured predicate scoping which related rows the aggregate spans (required for any/all, where it is the quantified predicate). A portable attr.filter object (eq/ne/in/isNull with and/or), desugared to canonical { field: { op: value } } at parse time; codegen renders it per target (e.g. SQL FILTER (WHERE ...) or SQLite CASE WHEN for a relational view)." },
31
- { "type": "attr", "subType": "boolean", "name": "distinct", "min": 0, "max": 1, "description": "Set (collect-only) to dedupe collected values (set semantics)." },
31
+ { "type": "attr", "subType": "boolean", "name": "distinct", "min": 0, "max": 1, "description": "Set (collect-only) to dedupe collected values (set semantics). Not supported on a whole-object collect (@of omitted): it is a guaranteed no-op whenever the value object carries the primary key, and a silent no-op is worse than a refusal." },
32
32
  { "type": "attr", "subType": "string", "name": "orderBy", "isArray": true, "min": 0, "max": 1, "description": "Ordering keys as 'field[:asc|desc]' (default asc) over the related entity's fields; nulls sort last. On @agg:collect sets element order (non-distinct only); on origin.first (required) selects the row. Semantic — carries no SQL syntax." }
33
33
  ]
34
34
  },
@@ -250,7 +250,7 @@ wheels = [
250
250
 
251
251
  [[package]]
252
252
  name = "metaobjects"
253
- version = "0.23.2"
253
+ version = "0.24.1"
254
254
  source = { editable = "." }
255
255
  dependencies = [
256
256
  { name = "pyyaml" },
File without changes
File without changes