metaobjects 0.19.9__tar.gz → 0.20.13__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 (401) hide show
  1. {metaobjects-0.19.9 → metaobjects-0.20.13}/PKG-INFO +1 -1
  2. {metaobjects-0.19.9 → metaobjects-0.20.13}/pyproject.toml +1 -1
  3. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/cli.py +334 -3
  4. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/KNOWN_GAPS.md +6 -4
  5. metaobjects-0.20.13/src/metaobjects/codegen/metaobjects-config.schema.json +47 -0
  6. metaobjects-0.20.13/src/metaobjects/codegen/project_config.py +160 -0
  7. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/core_types.py +6 -0
  8. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/errors.py +5 -0
  9. metaobjects-0.20.13/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +163 -0
  10. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/validation_passes.py +32 -0
  11. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/provider.py +11 -1
  12. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/registry.py +34 -0
  13. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/__init__.py +19 -2
  14. metaobjects-0.20.13/tests/codegen/test_cli_config_gen.py +224 -0
  15. metaobjects-0.20.13/tests/codegen/test_cli_config_verify.py +154 -0
  16. metaobjects-0.20.13/tests/codegen/test_project_config.py +136 -0
  17. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_provider_composition_conformance.py +79 -2
  18. metaobjects-0.20.13/tests/loader/test_enum_extends_values_conflict.py +167 -0
  19. metaobjects-0.20.13/tests/unit/__init__.py +0 -0
  20. {metaobjects-0.19.9 → metaobjects-0.20.13}/uv.lock +1 -1
  21. {metaobjects-0.19.9 → metaobjects-0.20.13}/.gitignore +0 -0
  22. {metaobjects-0.19.9 → metaobjects-0.20.13}/LICENSE +0 -0
  23. {metaobjects-0.19.9 → metaobjects-0.20.13}/README.md +0 -0
  24. {metaobjects-0.19.9 → metaobjects-0.20.13}/hatch_build.py +0 -0
  25. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/__init__.py +0 -0
  26. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/agent_context/__init__.py +0 -0
  27. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/agent_context/scaffold.py +0 -0
  28. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/apidocs/__init__.py +0 -0
  29. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/apidocs/api_model.py +0 -0
  30. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/apidocs/builder.py +0 -0
  31. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/apidocs/naming.py +0 -0
  32. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/apidocs/paths.py +0 -0
  33. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/apidocs/renderer.py +0 -0
  34. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/attr_class_map.py +0 -0
  35. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/__init__.py +0 -0
  36. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/collision_names.py +0 -0
  37. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/config.py +0 -0
  38. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/constants.py +0 -0
  39. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  40. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  41. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/format.py +0 -0
  42. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  43. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generator.py +0 -0
  44. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generator_registry.py +0 -0
  45. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/__init__.py +0 -0
  46. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  47. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  48. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  49. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  50. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  51. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  52. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  53. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
  54. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  55. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/router_generator.py +0 -0
  56. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  57. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  58. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  59. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  60. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  61. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  62. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/runner.py +0 -0
  63. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  64. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  65. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  66. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  67. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  68. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  69. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/codegen/type_map.py +0 -0
  70. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/datatype.py +0 -0
  71. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/documentation/__init__.py +0 -0
  72. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/documentation/doc_constants.py +0 -0
  73. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/documentation/doc_provider.py +0 -0
  74. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/documentation/doc_schema.py +0 -0
  75. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/__init__.py +0 -0
  76. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/merge.py +0 -0
  77. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/meta_data_loader.py +0 -0
  78. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/registered_validation.py +0 -0
  79. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/sources/__init__.py +0 -0
  80. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/sources/directory_source.py +0 -0
  81. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/sources/file_source.py +0 -0
  82. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  83. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/sources/uri_source.py +0 -0
  84. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/validate_discriminator.py +0 -0
  85. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/validate_field_readonly.py +0 -0
  86. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  87. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  88. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  89. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/__init__.py +0 -0
  90. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/__init__.py +0 -0
  91. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  92. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  93. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  94. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/field/__init__.py +0 -0
  95. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  96. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  97. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  98. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  99. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  100. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/index/__init__.py +0 -0
  101. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  102. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  103. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/object/__init__.py +0 -0
  104. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  105. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  106. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  107. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  108. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  109. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/object/value_object.py +0 -0
  110. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  111. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  112. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  113. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  114. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  115. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  116. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/meta_data.py +0 -0
  117. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/meta_root.py +0 -0
  118. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/__init__.py +0 -0
  119. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  120. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  121. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  122. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  123. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  124. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  125. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  126. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  127. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  128. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/__init__.py +0 -0
  129. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  130. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  131. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  132. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  133. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  134. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  135. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  136. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  137. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/provider_extends.py +0 -0
  138. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/template/__init__.py +0 -0
  139. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/template/meta_template.py +0 -0
  140. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  141. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/meta/template/template_constants.py +0 -0
  142. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/naming_refs.py +0 -0
  143. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/parser.py +0 -0
  144. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/parser_yaml.py +0 -0
  145. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/py.typed +0 -0
  146. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/registry_manifest.py +0 -0
  147. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/__init__.py +0 -0
  148. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/email_document.py +0 -0
  149. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/escapers.py +0 -0
  150. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  151. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/__init__.py +0 -0
  152. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/coerce.py +0 -0
  153. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/extract.py +0 -0
  154. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/extract_map.py +0 -0
  155. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  156. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/locate.py +0 -0
  157. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/normalize.py +0 -0
  158. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/strip.py +0 -0
  159. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/types.py +0 -0
  160. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  161. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/filesystem_provider.py +0 -0
  162. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/prompt/__init__.py +0 -0
  163. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  164. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  165. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  166. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  167. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  168. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/renderer.py +0 -0
  169. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/render/verify.py +0 -0
  170. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/runtime/__init__.py +0 -0
  171. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/runtime/llm_recorder.py +0 -0
  172. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  173. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/runtime/object_manager.py +0 -0
  174. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/runtime/tph.py +0 -0
  175. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/serializer_json.py +0 -0
  176. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/shared/__init__.py +0 -0
  177. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/shared/base_types.py +0 -0
  178. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/shared/separators.py +0 -0
  179. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/shared/structural.py +0 -0
  180. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/source/__init__.py +0 -0
  181. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/source/error_source.py +0 -0
  182. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/source/json_path.py +0 -0
  183. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/source/semantic_diff.py +0 -0
  184. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/source/yaml_positions.py +0 -0
  185. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/attr.json +0 -0
  186. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/db.json +0 -0
  187. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  188. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/field.json +0 -0
  189. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/identity.json +0 -0
  190. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/index.json +0 -0
  191. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/layout.json +0 -0
  192. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/object.json +0 -0
  193. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/origin.json +0 -0
  194. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  195. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  196. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/source.json +0 -0
  197. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/template.json +0 -0
  198. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  199. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/ui.json +0 -0
  200. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/validator.json +0 -0
  201. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/spec_metamodel/view.json +0 -0
  202. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/super_resolve.py +0 -0
  203. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/validation_types.py +0 -0
  204. {metaobjects-0.19.9 → metaobjects-0.20.13}/src/metaobjects/yaml_desugar.py +0 -0
  205. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/__init__.py +0 -0
  206. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/__init__.py +0 -0
  207. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  208. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/extends/expected/Program.py +0 -0
  209. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/extends/meta.json +0 -0
  210. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  211. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  212. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/nested-array/meta.json +0 -0
  213. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  214. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  215. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/scalars/meta.json +0 -0
  216. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  217. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/golden/vanilla/meta.json +0 -0
  218. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_abstract_conformance.py +0 -0
  219. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_api_docs_builder.py +0 -0
  220. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_api_docs_paths.py +0 -0
  221. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_cli.py +0 -0
  222. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_cli_providers.py +0 -0
  223. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_cli_registry.py +0 -0
  224. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  225. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_cli_verify_strict.py +0 -0
  226. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  227. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_constants_config.py +0 -0
  228. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_entity_model.py +0 -0
  229. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_enum_conformance.py +0 -0
  230. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  231. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_extract_tier_collision.py +0 -0
  232. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_extractor_generator.py +0 -0
  233. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  234. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_format.py +0 -0
  235. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  236. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_fr010_output_codegen.py +0 -0
  237. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  238. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_generator.py +0 -0
  239. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_generator_extension_seams.py +0 -0
  240. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_golden.py +0 -0
  241. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  242. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_inheritance_conformance.py +0 -0
  243. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_instance_artifacts.py +0 -0
  244. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_m2m_codegen.py +0 -0
  245. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_origin_nullability.py +0 -0
  246. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_output_parser_generator.py +0 -0
  247. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_overwrite_policy.py +0 -0
  248. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_payload_vo_generator.py +0 -0
  249. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_projection_compile.py +0 -0
  250. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_render_helper_conformance.py +0 -0
  251. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_render_helper_generator.py +0 -0
  252. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_reverse_finders.py +0 -0
  253. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_router_autoset.py +0 -0
  254. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_router_generator.py +0 -0
  255. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_runner.py +0 -0
  256. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_template_data.py +0 -0
  257. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_template_generator.py +0 -0
  258. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_template_output_pattern.py +0 -0
  259. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_template_scope_helpers.py +0 -0
  260. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_template_scope_walk.py +0 -0
  261. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_template_spec.py +0 -0
  262. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_tph_codegen.py +0 -0
  263. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_trace_helper_generator.py +0 -0
  264. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_type_map.py +0 -0
  265. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_validation_conformance.py +0 -0
  266. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/codegen/test_write_through.py +0 -0
  267. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/__init__.py +0 -0
  268. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/capabilities.py +0 -0
  269. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/conformance-expected-failures.json +0 -0
  270. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/conformance_adapter.py +0 -0
  271. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/corpus.py +0 -0
  272. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/expected_failures.py +0 -0
  273. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/fixture_discovery.py +0 -0
  274. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/navigator.py +0 -0
  275. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  276. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_conformance.py +0 -0
  277. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_extract_conformance.py +0 -0
  278. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_extract_object_verdict.py +0 -0
  279. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  280. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_fr011_attrs.py +0 -0
  281. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_generator_registry_conformance.py +0 -0
  282. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_object_model_conformance.py +0 -0
  283. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_registry_conformance.py +0 -0
  284. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_runner_hardfail.py +0 -0
  285. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  286. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_strict_attr_load.py +0 -0
  287. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_template_codegen_conformance.py +0 -0
  288. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_template_generator_conformance.py +0 -0
  289. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/test_yaml_conformance.py +0 -0
  290. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  291. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/__init__.py +0 -0
  292. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/api_contract_assertions.py +0 -0
  293. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/api_contract_jsonb_server.py +0 -0
  294. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/api_contract_m2m_server.py +0 -0
  295. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/api_contract_server.py +0 -0
  296. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/generated_jsonb_app.py +0 -0
  297. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/generated_m2m_app.py +0 -0
  298. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/generated_router_app.py +0 -0
  299. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/generated_tph_app.py +0 -0
  300. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/meta_ai_trace.yaml +0 -0
  301. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/normalization.py +0 -0
  302. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/postgres_container.py +0 -0
  303. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/query_runner.py +0 -0
  304. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/scenarios.py +0 -0
  305. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_api_contract.py +0 -0
  306. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_api_contract_generated.py +0 -0
  307. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_api_contract_jsonb.py +0 -0
  308. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_api_contract_m2m.py +0 -0
  309. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  310. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_api_contract_tph_generated.py +0 -0
  311. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  312. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_llm_call_trace.py +0 -0
  313. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_normalization.py +0 -0
  314. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_query_scenarios.py +0 -0
  315. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/integration/test_runtime_return_types.py +0 -0
  316. {metaobjects-0.19.9/tests/render → metaobjects-0.20.13/tests/loader}/__init__.py +0 -0
  317. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/open_closed_proof_test.py +0 -0
  318. {metaobjects-0.19.9/tests/render/extract → metaobjects-0.20.13/tests/render}/__init__.py +0 -0
  319. {metaobjects-0.19.9/tests/render/prompt → metaobjects-0.20.13/tests/render/extract}/__init__.py +0 -0
  320. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_coerce.py +0 -0
  321. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_extract.py +0 -0
  322. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_extract_map.py +0 -0
  323. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  324. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_locate.py +0 -0
  325. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_model.py +0 -0
  326. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_normalize.py +0 -0
  327. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_strip.py +0 -0
  328. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  329. {metaobjects-0.19.9/tests/source → metaobjects-0.20.13/tests/render/prompt}/__init__.py +0 -0
  330. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/prompt/test_output_format_renderer.py +0 -0
  331. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/test_email_document.py +0 -0
  332. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/test_filesystem_provider.py +0 -0
  333. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/test_output_format_renderer_nested.py +0 -0
  334. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/test_output_prompt_conformance.py +0 -0
  335. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/test_render_conformance.py +0 -0
  336. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/test_render_max_chars.py +0 -0
  337. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/test_verify.py +0 -0
  338. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/render/test_verify_conformance.py +0 -0
  339. {metaobjects-0.19.9/tests/unit → metaobjects-0.20.13/tests/source}/__init__.py +0 -0
  340. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/source/test_fr5c_merge_attribution.py +0 -0
  341. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/source/test_fr5d_reference_resolution.py +0 -0
  342. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/source/test_fr5e_database_source_shape.py +0 -0
  343. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/source/test_json_path.py +0 -0
  344. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/source/test_semantic_diff.py +0 -0
  345. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/source/test_source_on_node.py +0 -0
  346. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/source/test_yaml_positions.py +0 -0
  347. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/test_api_docs_accuracy.py +0 -0
  348. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_agent_context_staleness.py +0 -0
  349. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_capabilities.py +0 -0
  350. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_common_attrs.py +0 -0
  351. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_core_types.py +0 -0
  352. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_effective_package.py +0 -0
  353. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_errors.py +0 -0
  354. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_field_enum.py +0 -0
  355. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_field_map_validation.py +0 -0
  356. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  357. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  358. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_index_lookup.py +0 -0
  359. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_llm_recorder.py +0 -0
  360. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_loader.py +0 -0
  361. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_loader_bom.py +0 -0
  362. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_loader_class.py +0 -0
  363. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_merge.py +0 -0
  364. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_meta_attr.py +0 -0
  365. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_meta_data.py +0 -0
  366. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_meta_source.py +0 -0
  367. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_module_shortcuts.py +0 -0
  368. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_n2m_resolver.py +0 -0
  369. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  370. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  371. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  372. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_object_manager_write_through.py +0 -0
  373. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_one_primary_source.py +0 -0
  374. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_parser.py +0 -0
  375. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_parser_yaml.py +0 -0
  376. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_provider.py +0 -0
  377. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_provider_extension.py +0 -0
  378. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_registry.py +0 -0
  379. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_registry_completeness.py +0 -0
  380. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_registry_extend.py +0 -0
  381. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_registry_sealed.py +0 -0
  382. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_relationship_referential_actions.py +0 -0
  383. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_resolution_key.py +0 -0
  384. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  385. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_serializer.py +0 -0
  386. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_shared_constants.py +0 -0
  387. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_smoke.py +0 -0
  388. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_source_escapes.py +0 -0
  389. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_sources.py +0 -0
  390. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_strict_child_placement.py +0 -0
  391. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_super_resolve.py +0 -0
  392. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_template_toolcall.py +0 -0
  393. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  394. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_validation_attr_schema.py +0 -0
  395. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_validation_filter_values.py +0 -0
  396. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_validation_origin_195.py +0 -0
  397. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_validation_origin_paths.py +0 -0
  398. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_validation_sort_field.py +0 -0
  399. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_validation_warnings.py +0 -0
  400. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_write_coercion_arrays.py +0 -0
  401. {metaobjects-0.19.9 → metaobjects-0.20.13}/tests/unit/test_yaml_desugar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metaobjects
3
- Version: 0.19.9
3
+ Version: 0.20.13
4
4
  Summary: Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port.
5
5
  Project-URL: Homepage, https://metaobjects.dev
6
6
  Project-URL: Repository, https://github.com/metaobjectsdev/metaobjects
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "metaobjects"
3
- version = "0.19.9"
3
+ version = "0.20.13"
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"
@@ -39,7 +39,9 @@ generator-wiring divergence between the two commands.
39
39
  from __future__ import annotations
40
40
 
41
41
  import argparse
42
+ import dataclasses
42
43
  import json
44
+ import re
43
45
  import sys
44
46
  import tempfile
45
47
  from pathlib import Path
@@ -52,6 +54,13 @@ from metaobjects.agent_context import (
52
54
  )
53
55
  from metaobjects.meta.meta_data import MetaData
54
56
  from metaobjects.codegen.config import GenConfig
57
+ from metaobjects.codegen.project_config import (
58
+ CONFIG_FILENAME,
59
+ ConfigError,
60
+ ProjectConfig,
61
+ TargetConfig,
62
+ load_project_config,
63
+ )
55
64
  from metaobjects.codegen.generator import Generator
56
65
  from metaobjects.codegen.generators.entity_model import entity_model
57
66
  from metaobjects.codegen.generators.extractor_generator import extractor_generator
@@ -180,6 +189,48 @@ def _providers_from_args(args: argparse.Namespace) -> tuple[list[object], bool]:
180
189
  return providers, True
181
190
 
182
191
 
192
+ def _find_config(args: argparse.Namespace) -> Path | None:
193
+ """The config path: ``--config`` if given (even if missing → clear load error),
194
+ else ``./metaobjects.config.yaml`` in cwd when it exists, else ``None``."""
195
+ explicit = getattr(args, "config", None)
196
+ if explicit:
197
+ return Path(explicit)
198
+ default = Path.cwd() / CONFIG_FILENAME
199
+ return default if default.is_file() else None
200
+
201
+
202
+ def _config_providers(config: ProjectConfig) -> tuple[list[object], bool]:
203
+ """Resolve ``config.providers`` with the config file's directory on ``sys.path``.
204
+
205
+ #267: prepend the config directory so a ``module:symbol`` provider living beside
206
+ the config imports with no ``PYTHONPATH=``. Idempotent; the entry is left in
207
+ place (a short-lived CLI process). Prints resolution errors; returns (providers, ok).
208
+ """
209
+ config_dir = str(config.config_dir)
210
+ if config_dir not in sys.path:
211
+ sys.path.insert(0, config_dir)
212
+ providers, errors = _resolve_providers(config.providers)
213
+ if errors:
214
+ print("error: invalid provider in config:", file=sys.stderr)
215
+ for msg in errors:
216
+ print(f" {msg}", file=sys.stderr)
217
+ return providers, False
218
+ return providers, True
219
+
220
+
221
+ def _select_targets(
222
+ config: ProjectConfig, target_name: str | None
223
+ ) -> tuple[list[TargetConfig], str | None]:
224
+ """All targets, or the single ``--target`` (error string when the name is unknown)."""
225
+ if target_name is None:
226
+ return config.targets, None
227
+ t = config.target(target_name)
228
+ if t is None:
229
+ known = ", ".join(sorted(x.name for x in config.targets))
230
+ return [], f"unknown --target {target_name!r}; known targets: {known}"
231
+ return [t], None
232
+
233
+
183
234
  def _load_root(
184
235
  metadata_dir: str,
185
236
  strict: bool = False,
@@ -422,9 +473,14 @@ def _cmd_gen(args: argparse.Namespace) -> int:
422
473
 
423
474
  _warn_if_agent_context_stale()
424
475
 
425
- if args.metadata_dir is None or args.out is None:
476
+ # #267: config mode ⇔ no positional <metadata_dir>. The explicit
477
+ # <metadata_dir> + --out flag path below is untouched (byte-identical).
478
+ if args.metadata_dir is None:
479
+ return _cmd_gen_config(args)
480
+ if args.out is None:
426
481
  print(
427
- "error: gen requires <metadata_dir> and --out (or use --list).",
482
+ "error: gen requires <metadata_dir> and --out "
483
+ "(or a metaobjects.config.yaml / --list).",
428
484
  file=sys.stderr,
429
485
  )
430
486
  return 2
@@ -495,6 +551,100 @@ def _cmd_gen(args: argparse.Namespace) -> int:
495
551
  return 0
496
552
 
497
553
 
554
+ def _run_gen_targets(
555
+ config: ProjectConfig, targets: list[TargetConfig], root: MetaData
556
+ ) -> tuple[list[str], list[str]]:
557
+ """Run each target's suite into its ``outDir``. Returns (all_written, errors).
558
+
559
+ Cross-target duplicate-output-path guard (#267): ``run_gen``'s collision guard
560
+ is per-pass, so two targets writing the same full path would silently clobber
561
+ (generated files carry the @generated header). We accumulate every written full
562
+ path across targets and record an error when two targets emit the same one.
563
+ (Detection is post-write — the colliding file may already be on disk — but the
564
+ command still fails, so a misconfigured gate is caught in CI.)
565
+ """
566
+ all_written: list[str] = []
567
+ seen: dict[str, str] = {} # full path -> target name
568
+ errors: list[str] = []
569
+ for t in targets:
570
+ gens: list[Generator] | None = None
571
+ if t.generators is not None:
572
+ gens, gen_errors = _resolve_generators(",".join(t.generators))
573
+ if gen_errors:
574
+ errors.extend(f"target '{t.name}': {m}" for m in gen_errors)
575
+ continue
576
+ out_dir = config.out_dir_for(t)
577
+ try:
578
+ written = _run_suite(root, out_dir, gens, t.entities)
579
+ except ValueError as exc: # intra-target run_gen collision → clean error
580
+ errors.append(f"target '{t.name}': {exc}")
581
+ continue
582
+ for full in written:
583
+ if Path(full).name == "__init__.py":
584
+ continue # auto-emitted package marker — byte-identical across targets sharing an outDir; not a real collision
585
+ prior = seen.get(full)
586
+ if prior is not None and prior != t.name:
587
+ errors.append(
588
+ f"duplicate output path across targets: {full!r} written by "
589
+ f"both '{prior}' and '{t.name}'."
590
+ )
591
+ else:
592
+ seen[full] = t.name
593
+ all_written.extend(written)
594
+ return all_written, errors
595
+
596
+
597
+ def _cmd_gen_config(args: argparse.Namespace) -> int:
598
+ """``gen`` with no positional ``metadata_dir`` → declarative-config mode (#267).
599
+
600
+ Load ``metaobjects.config.yaml``, load metadata ONCE, and run every target
601
+ (or ``--target``) into its own ``outDir`` with a cross-target duplicate-path
602
+ guard. Providers resolve relative to the config file (no ``PYTHONPATH=``).
603
+ """
604
+ config_path = _find_config(args)
605
+ if config_path is None:
606
+ print(
607
+ "error: no <metadata_dir> given and no metaobjects.config.yaml found. "
608
+ "Either pass <metadata_dir> --out (flag mode) or create a "
609
+ "metaobjects.config.yaml (or pass --config <path>).",
610
+ file=sys.stderr,
611
+ )
612
+ return 2
613
+ try:
614
+ config = load_project_config(config_path)
615
+ except ConfigError as exc:
616
+ print(f"error: {exc}", file=sys.stderr)
617
+ return 1
618
+
619
+ targets, target_err = _select_targets(config, getattr(args, "target", None))
620
+ if target_err:
621
+ print(f"error: {target_err}", file=sys.stderr)
622
+ return 1
623
+
624
+ providers, providers_ok = _config_providers(config)
625
+ if not providers_ok:
626
+ return 1
627
+
628
+ root, load_errors = _load_root(config.metadata_dir(), providers=providers)
629
+ if root is None:
630
+ print("error: failed to load metadata:", file=sys.stderr)
631
+ for msg in load_errors:
632
+ print(f" {msg}", file=sys.stderr)
633
+ return 1
634
+
635
+ written, errors = _run_gen_targets(config, targets, root)
636
+ if errors:
637
+ for msg in errors:
638
+ print(f"error: {msg}", file=sys.stderr)
639
+ return 1
640
+ for path in written:
641
+ print(path)
642
+ print(
643
+ f"metaobjects gen: wrote {len(written)} file(s) across {len(targets)} target(s)."
644
+ )
645
+ return 0
646
+
647
+
498
648
  def _relative_set(root: Path) -> dict[str, str]:
499
649
  """Map every ``*.py`` file under ``root`` to its content, keyed by rel path.
500
650
 
@@ -516,6 +666,10 @@ def _verify_codegen(args: argparse.Namespace) -> int:
516
666
  code path (gen-to-temp + diff), so drift can never be a generator-wiring
517
667
  divergence between the two commands.
518
668
  """
669
+ # #267: config mode ⇔ no positional <metadata_dir>. The legacy
670
+ # <metadata_dir> + --out diff below is untouched.
671
+ if args.metadata_dir is None:
672
+ return _verify_codegen_config(args)
519
673
  if args.out is None:
520
674
  print(
521
675
  "error: verify --codegen requires --out (the committed output dir).",
@@ -572,6 +726,139 @@ def _verify_codegen(args: argparse.Namespace) -> int:
572
726
  return 1
573
727
 
574
728
 
729
+ def _temp_slot_for(temp_root: Path, real_outdir: str, config_dir: Path) -> str:
730
+ """The temp mirror of a committed ``outDir``.
731
+
732
+ Keyed by the real outDir's path relative to ``config_dir`` when it lives under
733
+ it, else a sanitized flat name — mirroring the TS ``computeCodegenDrift``
734
+ ``tempFor``. Two targets sharing a real outDir map to the SAME slot so their
735
+ co-resident regen accumulates into one tree: the diff is then union-of-
736
+ co-resident-regen vs the shared committed dir, so the other target's files are
737
+ never false ``extra`` drift.
738
+ """
739
+ try:
740
+ rel = Path(real_outdir).relative_to(config_dir)
741
+ except ValueError:
742
+ rel = None
743
+ if rel is None:
744
+ safe = re.sub(r"[^A-Za-z0-9]+", "_", real_outdir)
745
+ return str(temp_root / safe)
746
+ return str(temp_root / rel)
747
+
748
+
749
+ def _verify_codegen_config(args: argparse.Namespace) -> int:
750
+ """``verify --codegen`` with no positional ``metadata_dir`` → config mode (#267).
751
+
752
+ Symmetric with ``gen`` config mode: ONE whole-selection regen into a temp tree
753
+ (reusing :func:`_run_gen_targets` — the exact gen pipeline, so verify rejects
754
+ exactly what gen rejects, including the cross-target duplicate-output-path
755
+ guard), then a diff per UNIQUE real outDir. Targets that share an outDir are
756
+ verified TOGETHER — the diff is the union of their co-resident regen vs the
757
+ shared committed dir, so a shared outDir is never a false-positive ``extra``
758
+ drift (mirrors the TS ``computeCodegenDrift`` unit = unique outDir).
759
+ ``--target`` widens to the outDir-sharing closure (an outDir is verified as a
760
+ unit). Strict-by-default (ADR-0023) unless ``--lax``.
761
+ """
762
+ config_path = _find_config(args)
763
+ if config_path is None:
764
+ print(
765
+ "error: verify --codegen with no <metadata_dir> requires a "
766
+ "metaobjects.config.yaml (or --config <path>).",
767
+ file=sys.stderr,
768
+ )
769
+ return 2
770
+ try:
771
+ config = load_project_config(config_path)
772
+ except ConfigError as exc:
773
+ print(f"error: {exc}", file=sys.stderr)
774
+ return 1
775
+
776
+ selected, target_err = _select_targets(config, getattr(args, "target", None))
777
+ if target_err:
778
+ print(f"error: {target_err}", file=sys.stderr)
779
+ return 1
780
+
781
+ # Widen to the outDir-sharing closure: a shared outDir is verified as a unit,
782
+ # so --target a (whose outDir b also writes) pulls b in.
783
+ selected_outdirs = {config.out_dir_for(t) for t in selected}
784
+ closure = [t for t in config.targets if config.out_dir_for(t) in selected_outdirs]
785
+ added = [t.name for t in closure if t not in selected]
786
+ if added:
787
+ target_arg = getattr(args, "target", None)
788
+ print(
789
+ f"note: --target {target_arg!r} shares an outDir with "
790
+ f"{', '.join(added)}; verifying the shared outDir as a unit.",
791
+ file=sys.stderr,
792
+ )
793
+
794
+ strict = not getattr(args, "lax", False)
795
+ providers, providers_ok = _config_providers(config)
796
+ if not providers_ok:
797
+ return 1
798
+
799
+ root, load_errors = _load_root(config.metadata_dir(), strict=strict, providers=providers)
800
+ if root is None:
801
+ print("error: failed to load metadata:", file=sys.stderr)
802
+ for msg in load_errors:
803
+ print(f" {msg}", file=sys.stderr)
804
+ if strict and any("ERR_UNKNOWN_ATTR" in m for m in load_errors):
805
+ print(_strict_load_hint(), file=sys.stderr)
806
+ return 1
807
+
808
+ with tempfile.TemporaryDirectory() as temp_root:
809
+ # Map each unique real outDir to ONE temp slot (shared real dir => shared slot).
810
+ temp_for: dict[str, str] = {}
811
+ for t in closure:
812
+ real = config.out_dir_for(t)
813
+ if real not in temp_for:
814
+ temp_for[real] = _temp_slot_for(Path(temp_root), real, config.config_dir)
815
+ remapped = [
816
+ dataclasses.replace(t, out_dir=temp_for[config.out_dir_for(t)]) for t in closure
817
+ ]
818
+
819
+ # The exact gen pipeline into the temp tree — verify now rejects exactly
820
+ # what gen rejects (incl. the cross-target duplicate-output-path guard).
821
+ _written, errors = _run_gen_targets(config, remapped, root)
822
+ if errors:
823
+ for msg in errors:
824
+ print(f"error: {msg}", file=sys.stderr)
825
+ return 1
826
+
827
+ exit_code = 0
828
+ for real_outdir in sorted(temp_for):
829
+ tmp_slot = temp_for[real_outdir]
830
+ expected = _relative_set(Path(tmp_slot))
831
+ committed = _relative_set(Path(real_outdir))
832
+
833
+ changed = sorted(
834
+ k for k in expected if k in committed and expected[k] != committed[k]
835
+ )
836
+ missing = sorted(k for k in expected if k not in committed)
837
+ extra = sorted(k for k in committed if k not in expected)
838
+
839
+ names = "+".join(
840
+ t.name for t in closure if config.out_dir_for(t) == real_outdir
841
+ )
842
+ if not changed and not missing and not extra:
843
+ print(
844
+ f"metaobjects verify [{names}]: in sync ({len(expected)} file(s))."
845
+ )
846
+ continue
847
+ exit_code = max(exit_code, 1)
848
+ print(
849
+ f"error: [{names}] generated code is out of sync with metadata.",
850
+ file=sys.stderr,
851
+ )
852
+ for k in changed:
853
+ print(f" drifted: {k}", file=sys.stderr)
854
+ for k in missing:
855
+ print(f" missing: {k}", file=sys.stderr)
856
+ for k in extra:
857
+ print(f" extra: {k}", file=sys.stderr)
858
+ print("regenerate (metaobjects gen) and commit the result.", file=sys.stderr)
859
+ return exit_code
860
+
861
+
575
862
  #: The text-ref attrs a ``template.*`` node may carry. Each is resolved through
576
863
  #: the filesystem provider and run through the render ``verify()`` gate. A prompt
577
864
  #: / document uses ``@textRef``; an email uses subject / html / (optional) text
@@ -607,6 +894,14 @@ def _verify_templates(args: argparse.Namespace) -> int:
607
894
  (exit 1). Clean → 0. Reuses the render engine + field-tree walk; nothing is
608
895
  reimplemented here.
609
896
  """
897
+ if args.metadata_dir is None:
898
+ print(
899
+ "error: verify --templates requires <metadata_dir> (it is not "
900
+ "config-driven; the config's targets registry drives --codegen only).",
901
+ file=sys.stderr,
902
+ )
903
+ return 2
904
+
610
905
  template_root = getattr(args, "templates_root", None)
611
906
  if not template_root:
612
907
  print(
@@ -855,6 +1150,20 @@ def _build_parser() -> argparse.ArgumentParser:
855
1150
  "resolves through the standalone CLI (#158)"
856
1151
  ),
857
1152
  )
1153
+ gen.add_argument(
1154
+ "--config",
1155
+ default=None,
1156
+ help=(
1157
+ "path to a metaobjects.config.yaml (declarative targets registry). "
1158
+ "Config mode runs when no <metadata_dir> is given; default lookup is "
1159
+ "./metaobjects.config.yaml in cwd."
1160
+ ),
1161
+ )
1162
+ gen.add_argument(
1163
+ "--target",
1164
+ default=None,
1165
+ help="run only this named target from the config (default: every target)",
1166
+ )
858
1167
  gen.set_defaults(func=_cmd_gen)
859
1168
 
860
1169
  docs = sub.add_parser(
@@ -902,7 +1211,15 @@ def _build_parser() -> argparse.ArgumentParser:
902
1211
  "(ADR-0021 D2); bare verify defaults to --codegen"
903
1212
  ),
904
1213
  )
905
- verify.add_argument("metadata_dir", help="directory of metadata JSON/YAML files")
1214
+ verify.add_argument(
1215
+ "metadata_dir",
1216
+ nargs="?",
1217
+ default=None,
1218
+ help=(
1219
+ "directory of metadata JSON/YAML files. Omit to use a "
1220
+ "metaobjects.config.yaml (--codegen config mode, #267)."
1221
+ ),
1222
+ )
906
1223
  verify.add_argument(
907
1224
  "--codegen",
908
1225
  action="store_true",
@@ -953,6 +1270,20 @@ def _build_parser() -> argparse.ArgumentParser:
953
1270
  metavar="module:symbol",
954
1271
  help="load a consumer metadata Provider as 'module:symbol' (repeatable; #158)",
955
1272
  )
1273
+ verify.add_argument(
1274
+ "--config",
1275
+ default=None,
1276
+ help=(
1277
+ "path to a metaobjects.config.yaml. Config mode runs when no "
1278
+ "<metadata_dir> is given; default ./metaobjects.config.yaml in cwd. "
1279
+ "Drives --codegen per-target regen+diff."
1280
+ ),
1281
+ )
1282
+ verify.add_argument(
1283
+ "--target",
1284
+ default=None,
1285
+ help="verify only this named target from the config (default: every target)",
1286
+ )
956
1287
  verify.set_defaults(func=_cmd_verify)
957
1288
 
958
1289
  agent_docs = sub.add_parser(
@@ -47,7 +47,9 @@ the path-resolution and import-base machinery that is not yet on the
47
47
  Python codegen surface). Consumers wanting strong typing can hand-edit
48
48
  the generated router to import their preferred entity shape.
49
49
 
50
- **When it ships:** once the Python codegen grows per-target output
51
- directories (mirroring the TS `targets` registry — see
52
- `@metaobjectsdev/cli` README), the router generator will emit
53
- `from .<snake>_entity import <Entity>` and use the entity type throughout.
50
+ **When it ships:** the Python codegen now has a per-target output-directory
51
+ targets registry via the declarative `metaobjects.config.yaml` (#267 — mirrors
52
+ the TS `targets` registry). The remaining deferred piece is the router→entity-model
53
+ import wiring itself (`from .<snake>_entity import <Entity>`), which still needs the
54
+ path-resolution/import-base machinery; the config's per-target `outDir` is the
55
+ prerequisite that is now in place.
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://metaobjects.dev/schemas/metaobjects-config.schema.json",
4
+ "title": "MetaObjects Python codegen config",
5
+ "description": "Declarative config for the `metaobjects` Python CLI (#267). Schema keys mirror the TS metaobjects.config.ts vocabulary (outDir/generators/entities/providers/metadata).",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["targets"],
9
+ "properties": {
10
+ "metadata": {
11
+ "type": "string",
12
+ "description": "Metadata directory, relative to this config file. Default: metaobjects",
13
+ "default": "metaobjects"
14
+ },
15
+ "providers": {
16
+ "type": "array",
17
+ "items": { "type": "string", "pattern": "^[^:]+:[^:]+$" },
18
+ "description": "Consumer provider refs as 'module:symbol', resolved relative to this config file's directory (no PYTHONPATH needed)."
19
+ },
20
+ "targets": {
21
+ "type": "object",
22
+ "minProperties": 1,
23
+ "description": "Named run-specs; each key is a target name.",
24
+ "additionalProperties": {
25
+ "type": "object",
26
+ "additionalProperties": false,
27
+ "required": ["outDir"],
28
+ "properties": {
29
+ "outDir": {
30
+ "type": "string",
31
+ "description": "Output directory, relative to this config file."
32
+ },
33
+ "generators": {
34
+ "type": "array",
35
+ "items": { "type": "string" },
36
+ "description": "Stable generator names (see `metaobjects gen --list`). Omit to run the default suite."
37
+ },
38
+ "entities": {
39
+ "type": "array",
40
+ "items": { "type": "string" },
41
+ "description": "Entity-name allowlist. Omit to emit every entity."
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,160 @@
1
+ """Declarative project config for the Python codegen CLI (#267).
2
+
3
+ `metaobjects.config.yaml` describes a project's codegen surface once — a targets
4
+ registry (per-target ``outDir`` + generator selection + entity allowlist), the
5
+ metadata dir, and consumer providers — so ``metaobjects gen`` / ``verify --codegen``
6
+ run every target with no flags and provider modules resolve relative to the config
7
+ (no ``PYTHONPATH=``). YAML, not ``.py``: Python's config surface is pure data (the
8
+ provider CODE stays in its module, referenced by ``module:symbol`` per #158) —
9
+ unlike TS's executable ``metaobjects.config.ts`` (ADR-0034 owned generators / live
10
+ providers).
11
+
12
+ Schema keys are IDENTICAL to the TS ``metaobjects.config.ts`` vocabulary
13
+ (``targets.<name>.{outDir, generators, entities}``, ``providers``, ``metadata``) so
14
+ a polyglot adopter learns one vocabulary; the file SURFACE differs per port, the
15
+ SCHEMA does not (ADR-0021 D3 / FR-025). A JSON Schema ships beside this loader
16
+ (``metaobjects-config.schema.json``) for editor autocomplete + non-Python
17
+ validation.
18
+
19
+ Python-only, additive: no metamodel/vocabulary change; the existing positional
20
+ ``metadata_dir`` + ``--out`` flag path is untouched and byte-identical.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ from dataclasses import dataclass
25
+ from pathlib import Path
26
+
27
+ import yaml # type: ignore[import-untyped] # PyYAML ships no type stubs
28
+
29
+ #: Config filename looked up in the cwd when ``--config`` is not given.
30
+ CONFIG_FILENAME = "metaobjects.config.yaml"
31
+
32
+ #: Default metadata directory (relative to the config file) when ``metadata:`` is omitted.
33
+ DEFAULT_METADATA_DIR = "metaobjects"
34
+
35
+
36
+ class ConfigError(ValueError):
37
+ """A ``metaobjects.config.yaml`` that is missing, malformed, or invalid.
38
+
39
+ Carries a single user-facing message (no stack trace) — the CLI prints it and
40
+ exits non-zero.
41
+ """
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class TargetConfig:
46
+ """One named run-spec: where to write + which generators/entities to run.
47
+
48
+ NOTE (cross-port reconciliation, design §Schema): TS ``targets`` are pure output
49
+ DESTINATIONS (a generator picks a target; ``TargetConfig`` carries no selection),
50
+ while a declarative-port target is a destination PLUS a selection (``generators``
51
+ + ``entities``). The shared keys (``outDir``/``generators``/``entities``) stay
52
+ identical.
53
+ """
54
+
55
+ #: The target's map key (``targets.<name>``).
56
+ name: str
57
+ #: Output directory, relative to the config file's directory (or absolute).
58
+ out_dir: str
59
+ #: Stable generator names (registry ids); ``None`` => the default suite.
60
+ generators: list[str] | None
61
+ #: Entity-name allowlist; ``None`` => every entity.
62
+ entities: list[str] | None
63
+
64
+
65
+ @dataclass(frozen=True)
66
+ class ProjectConfig:
67
+ #: Directory containing the config file — the base for resolving ``metadata``,
68
+ #: ``providers`` (sys.path), and each target's ``outDir``.
69
+ config_dir: Path
70
+ #: Metadata directory (relative to ``config_dir`` or absolute).
71
+ metadata: str
72
+ #: Consumer provider refs (``module:symbol``), resolved config-relative.
73
+ providers: list[str]
74
+ #: Ordered run-specs (YAML map insertion order preserved).
75
+ targets: list[TargetConfig]
76
+
77
+ def metadata_dir(self) -> str:
78
+ """The metadata dir resolved against ``config_dir`` (absolute path string)."""
79
+ return _resolve_under(self.config_dir, self.metadata)
80
+
81
+ def target(self, name: str) -> TargetConfig | None:
82
+ return next((t for t in self.targets if t.name == name), None)
83
+
84
+ def out_dir_for(self, target: TargetConfig) -> str:
85
+ """``target.out_dir`` resolved against ``config_dir`` (absolute path string)."""
86
+ return _resolve_under(self.config_dir, target.out_dir)
87
+
88
+
89
+ def _resolve_under(base: Path, p: str) -> str:
90
+ q = Path(p)
91
+ return str(q if q.is_absolute() else (base / q).resolve())
92
+
93
+
94
+ def _require_str_list(value: object, ctx: str) -> list[str]:
95
+ if not isinstance(value, list) or not all(isinstance(x, str) for x in value):
96
+ raise ConfigError(f"{ctx} must be a list of strings.")
97
+ return list(value)
98
+
99
+
100
+ def load_project_config(path: Path) -> ProjectConfig:
101
+ """Parse + validate ``metaobjects.config.yaml`` at ``path``.
102
+
103
+ Raises :class:`ConfigError` (single user-facing message) on a missing file,
104
+ invalid YAML, or any shape violation. Defaults: ``metadata`` =>
105
+ ``DEFAULT_METADATA_DIR``, ``providers`` => ``[]``, per-target ``generators`` /
106
+ ``entities`` => ``None`` (default suite / all entities).
107
+ """
108
+ if not path.is_file():
109
+ raise ConfigError(f"config file not found: {path}")
110
+ try:
111
+ text = path.read_text(encoding="utf-8")
112
+ except (OSError, UnicodeDecodeError) as exc:
113
+ raise ConfigError(f"{path}: cannot read config file: {exc}") from exc
114
+ try:
115
+ raw = yaml.safe_load(text)
116
+ except yaml.YAMLError as exc:
117
+ raise ConfigError(f"{path}: invalid YAML: {exc}") from exc
118
+
119
+ if raw is None:
120
+ raise ConfigError(f"{path}: config is empty.")
121
+ if not isinstance(raw, dict):
122
+ raise ConfigError(f"{path}: top level must be a mapping.")
123
+
124
+ metadata = raw.get("metadata", DEFAULT_METADATA_DIR)
125
+ if not isinstance(metadata, str):
126
+ raise ConfigError(f"{path}: 'metadata' must be a string (a directory path).")
127
+
128
+ providers = _require_str_list(raw.get("providers", []), f"{path}: 'providers'")
129
+
130
+ targets_raw = raw.get("targets")
131
+ if not isinstance(targets_raw, dict) or not targets_raw:
132
+ raise ConfigError(
133
+ f"{path}: 'targets' must be a non-empty mapping of "
134
+ "<name> -> {outDir, generators?, entities?}."
135
+ )
136
+
137
+ targets: list[TargetConfig] = []
138
+ for name, spec in targets_raw.items():
139
+ ctx = f"{path}: target '{name}'"
140
+ if not isinstance(spec, dict):
141
+ raise ConfigError(f"{ctx} must be a mapping.")
142
+ out_dir = spec.get("outDir")
143
+ if not isinstance(out_dir, str) or not out_dir:
144
+ raise ConfigError(f"{ctx} must declare a non-empty 'outDir' string.")
145
+ generators = spec.get("generators")
146
+ if generators is not None:
147
+ generators = _require_str_list(generators, f"{ctx} 'generators'")
148
+ entities = spec.get("entities")
149
+ if entities is not None:
150
+ entities = _require_str_list(entities, f"{ctx} 'entities'")
151
+ targets.append(
152
+ TargetConfig(name=str(name), out_dir=out_dir, generators=generators, entities=entities)
153
+ )
154
+
155
+ return ProjectConfig(
156
+ config_dir=path.parent.resolve(),
157
+ metadata=metadata,
158
+ providers=providers,
159
+ targets=targets,
160
+ )
@@ -874,3 +874,9 @@ core_providers: list[Provider] = [
874
874
  prompt_provider,
875
875
  ui_provider,
876
876
  ]
877
+
878
+ # #265 — the ids of the LIBRARY providers above, as a frozen set. Consulted by
879
+ # spec_metamodel._apply_strict_attr_scoping (via TypeRegistry.attr_origin) to
880
+ # decide whether an attr's provenance is "library" (still prunable against the
881
+ # strict per-subtype allow-list) or a consumer's own provider (never pruned).
882
+ LIBRARY_PROVIDER_IDS: frozenset[str] = frozenset(p.id for p in core_providers)
@@ -180,6 +180,11 @@ class ErrorCode(str, Enum):
180
180
  ERR_SQL_BODY_WITH_UNMANAGED = "ERR_SQL_BODY_WITH_UNMANAGED"
181
181
  ERR_SQL_BODY_ON_WRITABLE_KIND = "ERR_SQL_BODY_ON_WRITABLE_KIND"
182
182
  ERR_ORIGIN_UNDER_SQL_BODY = "ERR_ORIGIN_UNDER_SQL_BODY"
183
+ # #246 — a field.enum both extends a shared package-level abstract enum and
184
+ # declares its own @values. One shared enum type has one member set — the
185
+ # own @values would be silently dropped in codegen. Remove the own @values
186
+ # to inherit the shared set, or extend a concrete (non-shared) enum instead.
187
+ ERR_ENUM_EXTENDS_VALUES_CONFLICT = "ERR_ENUM_EXTENDS_VALUES_CONFLICT"
183
188
  ERR_UNKNOWN = "ERR_UNKNOWN"
184
189
 
185
190