metaobjects 0.24.4__tar.gz → 0.25.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (440) hide show
  1. {metaobjects-0.24.4 → metaobjects-0.25.0}/PKG-INFO +1 -1
  2. {metaobjects-0.24.4 → metaobjects-0.25.0}/pyproject.toml +1 -1
  3. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/__init__.py +2 -0
  4. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/agent_context/scaffold.py +62 -3
  5. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/apidocs/builder.py +10 -4
  6. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/cli.py +260 -32
  7. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/KNOWN_GAPS.md +25 -0
  8. metaobjects-0.25.0/src/metaobjects/codegen/config.py +74 -0
  9. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generator_registry.py +8 -1
  10. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/entity_model.py +23 -10
  11. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +2 -15
  12. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/m2m_codegen.py +58 -33
  13. metaobjects-0.25.0/src/metaobjects/codegen/generators/names_generator.py +547 -0
  14. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/router_generator.py +28 -21
  15. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/instance_artifacts.py +5 -2
  16. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/metaobjects-config.schema.json +5 -0
  17. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/project_config.py +32 -0
  18. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/errors.py +7 -0
  19. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/meta_data.py +33 -6
  20. metaobjects-0.25.0/src/metaobjects/naming.py +136 -0
  21. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/parser.py +101 -2
  22. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/registry.py +8 -0
  23. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/registry_manifest.py +1 -1
  24. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/runtime/object_manager.py +68 -41
  25. metaobjects-0.25.0/src/metaobjects/source_resolution.py +105 -0
  26. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/attr.json +1 -1
  27. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/field.json +1 -1
  28. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/layout.json +1 -1
  29. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/object.json +1 -1
  30. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/origin.json +1 -1
  31. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/relationship.json +1 -1
  32. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/source.json +1 -1
  33. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/template.json +1 -1
  34. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/validator.json +1 -1
  35. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/view.json +1 -1
  36. metaobjects-0.25.0/tests/codegen/test_cli.py +346 -0
  37. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_cli_config_verify.py +106 -3
  38. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_cli_registry.py +2 -2
  39. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_entity_model.py +27 -14
  40. metaobjects-0.25.0/tests/codegen/test_m2m_codegen.py +383 -0
  41. metaobjects-0.25.0/tests/codegen/test_names_extends_chain.py +481 -0
  42. metaobjects-0.25.0/tests/codegen/test_names_generator.py +523 -0
  43. metaobjects-0.25.0/tests/codegen/test_no_magic_physical_names.py +922 -0
  44. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_project_config.py +74 -0
  45. metaobjects-0.25.0/tests/codegen/test_source_resolution.py +312 -0
  46. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_sourceless_projection.py +5 -7
  47. metaobjects-0.25.0/tests/loader/test_abstract_subtype_authored.py +159 -0
  48. metaobjects-0.25.0/tests/runtime/test_object_manager_table_name.py +87 -0
  49. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_agent_context_staleness.py +38 -0
  50. metaobjects-0.25.0/tests/unit/test_column_naming_strategy.py +105 -0
  51. metaobjects-0.25.0/tests/unit/test_effective_children_sibling_shadowing.py +176 -0
  52. metaobjects-0.25.0/tests/unit/test_no_dead_config_fields.py +100 -0
  53. {metaobjects-0.24.4 → metaobjects-0.25.0}/uv.lock +1 -1
  54. metaobjects-0.24.4/src/metaobjects/codegen/config.py +0 -27
  55. metaobjects-0.24.4/tests/codegen/test_cli.py +0 -163
  56. metaobjects-0.24.4/tests/codegen/test_m2m_codegen.py +0 -157
  57. {metaobjects-0.24.4 → metaobjects-0.25.0}/.gitignore +0 -0
  58. {metaobjects-0.24.4 → metaobjects-0.25.0}/LICENSE +0 -0
  59. {metaobjects-0.24.4 → metaobjects-0.25.0}/README.md +0 -0
  60. {metaobjects-0.24.4 → metaobjects-0.25.0}/hatch_build.py +0 -0
  61. {metaobjects-0.24.4 → metaobjects-0.25.0}/scripts/generate_embedded_library.py +0 -0
  62. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/agent_context/__init__.py +0 -0
  63. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/apidocs/__init__.py +0 -0
  64. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/apidocs/api_model.py +0 -0
  65. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/apidocs/naming.py +0 -0
  66. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/apidocs/paths.py +0 -0
  67. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/apidocs/renderer.py +0 -0
  68. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/attr_class_map.py +0 -0
  69. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/__init__.py +0 -0
  70. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/collision_names.py +0 -0
  71. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/constants.py +0 -0
  72. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  73. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  74. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/format.py +0 -0
  75. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  76. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generator.py +0 -0
  77. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
  78. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  79. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/find_inbound.py +0 -0
  80. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  81. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  82. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  83. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
  84. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  85. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  86. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  87. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  88. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  89. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  90. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/runner.py +0 -0
  91. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  92. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  93. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  94. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  95. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  96. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  97. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/codegen/type_map.py +0 -0
  98. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/config/__init__.py +0 -0
  99. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/config/neutral_config.py +0 -0
  100. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/config/source_resolver.py +0 -0
  101. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/core_types.py +0 -0
  102. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/datatype.py +0 -0
  103. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/documentation/__init__.py +0 -0
  104. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/documentation/doc_constants.py +0 -0
  105. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/documentation/doc_provider.py +0 -0
  106. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/documentation/doc_schema.py +0 -0
  107. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/library/__init__.py +0 -0
  108. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/library/embedded_library.py +0 -0
  109. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/library/library_sources.py +0 -0
  110. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/__init__.py +0 -0
  111. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/merge.py +0 -0
  112. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
  113. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/registered_validation.py +0 -0
  114. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/sources/__init__.py +0 -0
  115. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
  116. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/sources/file_source.py +0 -0
  117. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  118. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
  119. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/validate_discriminator.py +0 -0
  120. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
  121. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/validate_field_mutability.py +0 -0
  122. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  123. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  124. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  125. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/loader/validation_passes.py +0 -0
  126. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/__init__.py +0 -0
  127. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/__init__.py +0 -0
  128. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  129. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  130. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  131. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/field/__init__.py +0 -0
  132. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  133. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  134. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  135. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  136. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  137. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/index/__init__.py +0 -0
  138. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  139. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  140. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/object/__init__.py +0 -0
  141. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  142. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  143. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  144. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  145. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  146. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
  147. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  148. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  149. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  150. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  151. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
  152. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/requirement/meta_requirement.py +0 -0
  153. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/requirement/requirement_constants.py +0 -0
  154. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  155. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  156. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/meta_root.py +0 -0
  157. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/__init__.py +0 -0
  158. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  159. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  160. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  161. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  162. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  163. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  164. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  165. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  166. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  167. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/__init__.py +0 -0
  168. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  169. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  170. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  171. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  172. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  173. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  174. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  175. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  176. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/provider_extends.py +0 -0
  177. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/template/__init__.py +0 -0
  178. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/template/meta_template.py +0 -0
  179. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  180. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/meta/template/template_constants.py +0 -0
  181. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/naming_refs.py +0 -0
  182. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/parser_yaml.py +0 -0
  183. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/provider.py +0 -0
  184. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/py.typed +0 -0
  185. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/__init__.py +0 -0
  186. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/email_document.py +0 -0
  187. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/escapers.py +0 -0
  188. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  189. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/__init__.py +0 -0
  190. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/coerce.py +0 -0
  191. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/extract.py +0 -0
  192. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/extract_map.py +0 -0
  193. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  194. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/locate.py +0 -0
  195. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/normalize.py +0 -0
  196. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/strip.py +0 -0
  197. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/types.py +0 -0
  198. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  199. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/filesystem_provider.py +0 -0
  200. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/payload_accessors.py +0 -0
  201. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/prompt/__init__.py +0 -0
  202. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  203. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  204. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  205. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  206. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  207. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/renderer.py +0 -0
  208. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/render/verify.py +0 -0
  209. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/runtime/__init__.py +0 -0
  210. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
  211. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  212. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/runtime/tph.py +0 -0
  213. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/serializer_json.py +0 -0
  214. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/shared/__init__.py +0 -0
  215. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/shared/base_types.py +0 -0
  216. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/shared/separators.py +0 -0
  217. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/shared/structural.py +0 -0
  218. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/source/__init__.py +0 -0
  219. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/source/error_source.py +0 -0
  220. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/source/json_path.py +0 -0
  221. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/source/semantic_diff.py +0 -0
  222. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/source/yaml_positions.py +0 -0
  223. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  224. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/db.json +0 -0
  225. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  226. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
  227. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/index.json +0 -0
  228. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  229. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/requirement.json +0 -0
  230. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  231. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
  232. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/super_resolve.py +0 -0
  233. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/validation_types.py +0 -0
  234. {metaobjects-0.24.4 → metaobjects-0.25.0}/src/metaobjects/yaml_desugar.py +0 -0
  235. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/__init__.py +0 -0
  236. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/__init__.py +0 -0
  237. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  238. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/extends/expected/Program.py +0 -0
  239. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/extends/meta.json +0 -0
  240. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  241. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  242. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/nested-array/meta.json +0 -0
  243. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  244. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  245. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/scalars/meta.json +0 -0
  246. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  247. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/golden/vanilla/meta.json +0 -0
  248. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_abstract_conformance.py +0 -0
  249. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_api_docs_builder.py +0 -0
  250. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_api_docs_paths.py +0 -0
  251. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_cli_config_gen.py +0 -0
  252. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_cli_providers.py +0 -0
  253. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  254. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_cli_verify_strict.py +0 -0
  255. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  256. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_constants_config.py +0 -0
  257. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_enum_conformance.py +0 -0
  258. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  259. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_extract_tier_collision.py +0 -0
  260. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_extractor_generator.py +0 -0
  261. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  262. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_format.py +0 -0
  263. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  264. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
  265. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  266. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_generator.py +0 -0
  267. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_generator_extension_seams.py +0 -0
  268. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_golden.py +0 -0
  269. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  270. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_inheritance_conformance.py +0 -0
  271. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_instance_artifacts.py +0 -0
  272. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_origin_nullability.py +0 -0
  273. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_output_parser_generator.py +0 -0
  274. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_overwrite_policy.py +0 -0
  275. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_overwrite_policy_hash.py +0 -0
  276. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_payload_vo_generator.py +0 -0
  277. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_projection_compile.py +0 -0
  278. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_render_helper_conformance.py +0 -0
  279. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_render_helper_generator.py +0 -0
  280. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_reverse_finders.py +0 -0
  281. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_router_autoset.py +0 -0
  282. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_router_generator.py +0 -0
  283. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_runner.py +0 -0
  284. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_template_data.py +0 -0
  285. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_template_generator.py +0 -0
  286. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_template_output_pattern.py +0 -0
  287. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_template_scope_helpers.py +0 -0
  288. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_template_scope_walk.py +0 -0
  289. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_template_spec.py +0 -0
  290. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_tph_codegen.py +0 -0
  291. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_trace_helper_generator.py +0 -0
  292. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_type_map.py +0 -0
  293. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_validation_conformance.py +0 -0
  294. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/codegen/test_write_through.py +0 -0
  295. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/config/__init__.py +0 -0
  296. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/config/test_neutral_config.py +0 -0
  297. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/config/test_source_resolver.py +0 -0
  298. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/__init__.py +0 -0
  299. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/capabilities.py +0 -0
  300. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/conformance-expected-failures.json +0 -0
  301. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/conformance_adapter.py +0 -0
  302. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/corpus.py +0 -0
  303. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/expected_failures.py +0 -0
  304. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/fixture_discovery.py +0 -0
  305. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/navigator.py +0 -0
  306. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  307. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_conformance.py +0 -0
  308. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_extract_conformance.py +0 -0
  309. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_extract_object_verdict.py +0 -0
  310. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  311. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_fr011_attrs.py +0 -0
  312. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
  313. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_object_model_conformance.py +0 -0
  314. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_provider_composition_conformance.py +0 -0
  315. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_registry_conformance.py +0 -0
  316. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_runner_hardfail.py +0 -0
  317. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_source_resolution_conformance.py +0 -0
  318. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  319. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_strict_attr_load.py +0 -0
  320. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_template_codegen_conformance.py +0 -0
  321. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_template_generator_conformance.py +0 -0
  322. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/test_yaml_conformance.py +0 -0
  323. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  324. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/__init__.py +0 -0
  325. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/api_contract_assertions.py +0 -0
  326. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/api_contract_jsonb_server.py +0 -0
  327. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/api_contract_m2m_server.py +0 -0
  328. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/api_contract_server.py +0 -0
  329. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/generated_jsonb_app.py +0 -0
  330. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/generated_m2m_app.py +0 -0
  331. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/generated_router_app.py +0 -0
  332. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/generated_tph_app.py +0 -0
  333. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/meta_ai_trace.yaml +0 -0
  334. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/normalization.py +0 -0
  335. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/postgres_container.py +0 -0
  336. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/query_runner.py +0 -0
  337. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/scenarios.py +0 -0
  338. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_api_contract.py +0 -0
  339. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_api_contract_generated.py +0 -0
  340. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_api_contract_jsonb.py +0 -0
  341. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_api_contract_m2m.py +0 -0
  342. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  343. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
  344. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  345. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_llm_call_trace.py +0 -0
  346. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_normalization.py +0 -0
  347. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_query_scenarios.py +0 -0
  348. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/integration/test_runtime_return_types.py +0 -0
  349. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/loader/__init__.py +0 -0
  350. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/loader/test_enum_extends_values_conflict.py +0 -0
  351. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/loader/test_field_mutability_order.py +0 -0
  352. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/loader/test_shipped_library_ai.py +0 -0
  353. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/loader/test_template_payload_target_210.py +0 -0
  354. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/open_closed_proof_test.py +0 -0
  355. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/__init__.py +0 -0
  356. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/__init__.py +0 -0
  357. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_coerce.py +0 -0
  358. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_extract.py +0 -0
  359. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_extract_map.py +0 -0
  360. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  361. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_locate.py +0 -0
  362. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_model.py +0 -0
  363. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_normalize.py +0 -0
  364. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_strip.py +0 -0
  365. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  366. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/prompt/__init__.py +0 -0
  367. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
  368. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/test_email_document.py +0 -0
  369. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/test_filesystem_provider.py +0 -0
  370. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/test_output_format_renderer_nested.py +0 -0
  371. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/test_output_prompt_conformance.py +0 -0
  372. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/test_render_conformance.py +0 -0
  373. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/test_render_max_chars.py +0 -0
  374. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/test_verify.py +0 -0
  375. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/render/test_verify_conformance.py +0 -0
  376. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/runtime/test_object_manager_enum_intvaluemap.py +0 -0
  377. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/source/__init__.py +0 -0
  378. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
  379. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
  380. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
  381. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/source/test_json_path.py +0 -0
  382. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/source/test_semantic_diff.py +0 -0
  383. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/source/test_source_on_node.py +0 -0
  384. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/source/test_yaml_positions.py +0 -0
  385. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/test_api_docs_accuracy.py +0 -0
  386. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/__init__.py +0 -0
  387. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_capabilities.py +0 -0
  388. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_common_attrs.py +0 -0
  389. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_core_types.py +0 -0
  390. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_effective_package.py +0 -0
  391. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_errors.py +0 -0
  392. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_field_enum.py +0 -0
  393. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_field_enum_intvaluemap.py +0 -0
  394. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_field_map_validation.py +0 -0
  395. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  396. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  397. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_index_lookup.py +0 -0
  398. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_llm_recorder.py +0 -0
  399. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_loader.py +0 -0
  400. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_loader_bom.py +0 -0
  401. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_loader_class.py +0 -0
  402. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_merge.py +0 -0
  403. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_meta_attr.py +0 -0
  404. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_meta_data.py +0 -0
  405. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_meta_source.py +0 -0
  406. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_module_shortcuts.py +0 -0
  407. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_n2m_resolver.py +0 -0
  408. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  409. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  410. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  411. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_object_manager_write_through.py +0 -0
  412. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_one_primary_source.py +0 -0
  413. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_parser.py +0 -0
  414. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_parser_yaml.py +0 -0
  415. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_provider.py +0 -0
  416. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_provider_extension.py +0 -0
  417. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_registry.py +0 -0
  418. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_registry_completeness.py +0 -0
  419. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_registry_extend.py +0 -0
  420. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_registry_sealed.py +0 -0
  421. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_relationship_referential_actions.py +0 -0
  422. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_resolution_key.py +0 -0
  423. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  424. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_serializer.py +0 -0
  425. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_shared_constants.py +0 -0
  426. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_smoke.py +0 -0
  427. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_source_escapes.py +0 -0
  428. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_sources.py +0 -0
  429. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_strict_child_placement.py +0 -0
  430. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_super_resolve.py +0 -0
  431. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_template_toolcall.py +0 -0
  432. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  433. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_validation_attr_schema.py +0 -0
  434. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_validation_filter_values.py +0 -0
  435. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_validation_origin_195.py +0 -0
  436. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_validation_origin_paths.py +0 -0
  437. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_validation_sort_field.py +0 -0
  438. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_validation_warnings.py +0 -0
  439. {metaobjects-0.24.4 → metaobjects-0.25.0}/tests/unit/test_write_coercion_arrays.py +0 -0
  440. {metaobjects-0.24.4 → metaobjects-0.25.0}/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.4
3
+ Version: 0.25.0
4
4
  Summary: Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port.
5
5
  Project-URL: Homepage, https://metaobjects.dev
6
6
  Project-URL: Repository, https://github.com/metaobjectsdev/metaobjects
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "metaobjects"
3
- version = "0.24.4"
3
+ version = "0.25.0"
4
4
  description = "Cross-language metadata standard: declare typed entities once, generate idiomatic drift-checked code across languages — Python port."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -35,6 +35,7 @@ from .meta.core.object.object_extract import (
35
35
  extract_schema_for,
36
36
  )
37
37
  from .meta.core.object.value_object import ValueObject
38
+ from .naming import resolve_column_name
38
39
 
39
40
  # Module-level shortcuts: the 99% case for callers who don't need a
40
41
  # long-lived loader. Signatures + docstrings come straight from the
@@ -58,6 +59,7 @@ __all__ = [
58
59
  "load_directory",
59
60
  "load_uris",
60
61
  "load_string",
62
+ "resolve_column_name",
61
63
  # Runtime object model (Phase A)
62
64
  "ValueObject",
63
65
  "MetaObjectAware",
@@ -10,6 +10,7 @@ installed MetaObjects version.
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
+ import re
13
14
  from dataclasses import dataclass
14
15
  from importlib.metadata import PackageNotFoundError, version as _pkg_version
15
16
 
@@ -76,6 +77,61 @@ class Manifest:
76
77
  )
77
78
 
78
79
 
80
+ # The sentinel a port stamps when it cannot resolve its own installed version.
81
+ _UNRESOLVED_VERSION = "0.0.0"
82
+
83
+ _RELEASE_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
84
+
85
+
86
+ def _release_series(version: object) -> tuple[int, int] | None:
87
+ """Ordered release coordinate ``(minor, patch)``, or ``None`` when not orderable.
88
+
89
+ The MAJOR is deliberately dropped. It is a per-registry constant, not information:
90
+ npm/PyPI/NuGet ship ``0.<m>.<p>`` and Maven Central the same ``<m>.<p>`` on its
91
+ historical major ``7``, so minor.patch IS the shared release coordinate across all
92
+ four ports.
93
+
94
+ ``None`` means "not orderable, so nudge", and deliberately covers prereleases
95
+ (``0.24.5-rc.1``), build metadata (``0.24.5+abc``) and the ``0.0.0`` sentinel. Each
96
+ must keep nudging: an RC-scaffolded context against a final release is still worth
97
+ refreshing, and an unknown install must never assert "in sync".
98
+ """
99
+ if not isinstance(version, str):
100
+ return None
101
+ v = version.strip()
102
+ if v == _UNRESOLVED_VERSION:
103
+ return None
104
+ m = _RELEASE_RE.match(v)
105
+ if m is None:
106
+ return None
107
+ return int(m.group(2)), int(m.group(3))
108
+
109
+
110
+ def _context_is_ahead_of_install(generated_by: object, current_version: str) -> bool:
111
+ """True when the manifest was stamped by a release STRICTLY NEWER than the install.
112
+
113
+ The one exemption from "any drift nudges", and it exists because of the
114
+ publish-what-changed rule (docs/RELEASING.md): a registry publishes only when it has
115
+ a changed product file, so a port legitimately sits behind npm — while
116
+ ``meta agent-docs``, the canonical scaffolder for EVERY port, stamps the npm version
117
+ it was run from. A Python install at ``0.24.4`` whose context was scaffolded by npm
118
+ ``0.24.7`` is correct, and nudging it is issue #347 exactly: the remedy re-runs the
119
+ scaffolder, which re-stamps ``0.24.7``, so the advisory can never be satisfied and
120
+ fires on every build forever.
121
+
122
+ KNOWN BOUND, stated rather than hidden: ordering on minor.patch assumes both versions
123
+ sit in the same release SERIES. That holds for every release to date and after the
124
+ 1.0/8.0 cut, but not ACROSS it — there a ``0.24.x`` context against a ``1.0.0``
125
+ install reads as "ahead" and the nudge is suppressed once. A missed advisory, never a
126
+ wrong action.
127
+ """
128
+ stamped = _release_series(generated_by)
129
+ installed = _release_series(current_version)
130
+ if stamped is None or installed is None:
131
+ return False # not orderable → nudge
132
+ return stamped > installed
133
+
134
+
79
135
  def agent_context_staleness(
80
136
  manifest: dict[str, object] | None, current_version: str
81
137
  ) -> str | None:
@@ -85,15 +141,18 @@ def agent_context_staleness(
85
141
  is in sync — and a one-line advisory message otherwise. Pure + advisory:
86
142
  never raises, never blocks, never writes.
87
143
 
88
- The comparison is **exact equality** on purpose: ANY drift nudges (a
89
- re-scaffold is cheap + idempotent). Do NOT "fix" this into a semver compare —
90
- a prerelease/build-metadata difference is still a reason to refresh.
144
+ The comparison is **exact equality** first: ANY drift nudges (a re-scaffold is
145
+ cheap + idempotent), so this is not a semver compare — a prerelease or
146
+ build-metadata difference is still a reason to refresh. See
147
+ ``_context_is_ahead_of_install`` for the ONE case that is exempt.
91
148
  """
92
149
  if manifest is None:
93
150
  return None # no agent context here → nothing to nudge
94
151
  generated_by = manifest.get("generatedBy")
95
152
  if generated_by == current_version:
96
153
  return None # in sync
154
+ if _context_is_ahead_of_install(generated_by, current_version):
155
+ return None # scaffolded by a NEWER release than this install — see below
97
156
  frm = generated_by if generated_by else "an older MetaObjects"
98
157
  return (
99
158
  f"MetaObjects agent context was generated by {frm}; "
@@ -4,7 +4,7 @@ This is the ACCURATE-BY-CONSTRUCTION half of the api-docs pipeline: every
4
4
  documented symbol name comes from the :mod:`metaobjects.apidocs.naming` seam (never
5
5
  re-concatenated here — the SAME seam the real generators delegate to), and every
6
6
  inclusion decision comes from an applies-predicate that REUSES the corresponding
7
- generator's own gate helpers (``emits_instance_artifacts`` / ``_primary_source_rdb``
7
+ generator's own gate helpers (``emits_instance_artifacts`` / ``primary_rdb_source``
8
8
  + source kind / ``resolve_payload_vo`` / format check / ``is_tph_subtype``) rather
9
9
  than re-implementing the gate. The result is that what this builder documents ==
10
10
  what the generators emit, by SHARING the single source of truth.
@@ -47,9 +47,9 @@ from metaobjects.codegen.generators.payload_vo_generator import (
47
47
  is_field_required,
48
48
  resolve_payload_vo,
49
49
  )
50
- from metaobjects.codegen.generators.router_generator import _primary_source_rdb
51
50
  from metaobjects.codegen.generators.tph_plan import is_tph_subtype
52
51
  from metaobjects.codegen.instance_artifacts import emits_instance_artifacts, is_abstract
52
+ from metaobjects.source_resolution import primary_rdb_source
53
53
  from metaobjects.meta.core.field import field_constants as fc
54
54
  from metaobjects.meta.core.field.meta_field import MetaField
55
55
  from metaobjects.meta.core.object.meta_object import MetaObject
@@ -91,7 +91,7 @@ def _is_writable_table_entity(obj: MetaObject, object_index: dict[str, MetaObjec
91
91
  return False
92
92
  if is_tph_subtype(obj):
93
93
  return False
94
- src = _primary_source_rdb(obj)
94
+ src = primary_rdb_source(obj)
95
95
  if src is None:
96
96
  return False
97
97
  return src.effective_kind() == SOURCE_KIND_TABLE
@@ -261,7 +261,13 @@ class PythonApiModelBuilder:
261
261
  add("PUT " + item, "update (PUT alias)")
262
262
  add("DELETE " + item, "delete")
263
263
 
264
- # M:N traversal — GET /<source-plural>/{id}/<relation>.
264
+ # M:N traversal — GET /<source-plural>/{id}/<relation>. `docs` has no
265
+ # `GenConfig`/`--column-naming` of its own (see `cli.py`'s docs handler) and
266
+ # this loop reads only `.target_entity` / `.relation_name` off the
267
+ # descriptor — never a physical column/table name — so resolving it under
268
+ # the port default here changes nothing observable; threading a naming
269
+ # strategy through would mean inventing a config seam this command doesn't
270
+ # have, for a value nothing here would read.
265
271
  for d in resolve_m2m_descriptors(entity, object_index):
266
272
  target = object_index.get(d.target_entity)
267
273
  target_name = target.name if target is not None else d.target_entity
@@ -17,6 +17,9 @@ Two subcommands:
17
17
 
18
18
  --codegen regenerate into a temp dir + diff against the committed
19
19
  ``--out`` tree (Python's historical default behavior).
20
+ ``--column-naming`` must match the strategy ``gen`` was
21
+ run with (R31) or the ``names`` generator's constants
22
+ report spurious drift.
20
23
  --templates template/prompt ``{{field}}`` ↔ payload-VO drift — the
21
24
  render ``verify()`` gate — resolving each ``template.*``
22
25
  node's refs via a filesystem provider rooted at
@@ -55,6 +58,7 @@ from metaobjects.agent_context import (
55
58
  )
56
59
  from metaobjects.meta.meta_data import MetaData
57
60
  from metaobjects.codegen.config import GenConfig
61
+ from metaobjects.codegen.overwrite_policy import has_hash_manifest, read_generated_hash
58
62
  from metaobjects.codegen.project_config import (
59
63
  CONFIG_FILENAME,
60
64
  ConfigError,
@@ -68,6 +72,7 @@ from metaobjects.codegen.generators.extractor_generator import extractor_generat
68
72
  from metaobjects.codegen.generators.filter_allowlist_generator import (
69
73
  filter_allowlist_generator,
70
74
  )
75
+ from metaobjects.codegen.generators.names_generator import names_generator
71
76
  from metaobjects.codegen.generators.output_parser_generator import (
72
77
  output_parser_generator,
73
78
  )
@@ -87,6 +92,7 @@ from metaobjects.codegen.generators.render_helper_generator import (
87
92
  _resolve_payload_vo,
88
93
  )
89
94
  from metaobjects.meta.template import template_constants as tc
95
+ from metaobjects.naming import COLUMN_NAMING_STRATEGIES, DEFAULT_COLUMN_NAMING
90
96
  from metaobjects.render.filesystem_provider import FilesystemProvider
91
97
  from metaobjects.render.verify import (
92
98
  ERR_REQUIRED_SLOT_UNUSED,
@@ -117,6 +123,7 @@ def _default_generators() -> list[Generator]:
117
123
  entity_model(),
118
124
  router_generator(),
119
125
  filter_allowlist_generator(),
126
+ names_generator(),
120
127
  payload_vo_generator(),
121
128
  output_parser_generator(),
122
129
  output_prompt_generator(),
@@ -368,6 +375,7 @@ def _run_suite(
368
375
  entity_filter: list[str] | None = None,
369
376
  emit_package_init: bool = True,
370
377
  gen_state_dir: str | None = None,
378
+ column_naming: str = DEFAULT_COLUMN_NAMING,
371
379
  ) -> list[str]:
372
380
  """Run a generator suite against an ALREADY-LOADED ``root`` into ``out_dir``.
373
381
 
@@ -396,6 +404,7 @@ def _run_suite(
396
404
  out_dir=out_dir,
397
405
  emit_package_init=emit_package_init,
398
406
  gen_state_dir=gen_state_dir,
407
+ column_naming=column_naming,
399
408
  )
400
409
  suite = generators if generators is not None else _default_generators()
401
410
  result = run_gen(config, root, generators=suite, entity_filter=entity_filter)
@@ -412,6 +421,7 @@ def _generate(
412
421
  emit_package_init: bool = True,
413
422
  strict: bool = False,
414
423
  providers: list[object] | None = None,
424
+ column_naming: str = DEFAULT_COLUMN_NAMING,
415
425
  ) -> tuple[list[str], list[str]]:
416
426
  """Run the generator suite into ``out_dir``.
417
427
 
@@ -426,6 +436,10 @@ def _generate(
426
436
  ``(written_paths, errors)``. On a load error, ``errors`` is non-empty and no
427
437
  files are written. ``strict`` (ADR-0023, #96) is threaded to the load — the
428
438
  ``verify --codegen`` caller passes True; ``gen`` keeps the default False.
439
+ ``column_naming`` (R31) must match the strategy the committed output was
440
+ produced with, or the ``names`` generator's constants regenerate under the
441
+ wrong strategy and every ``<ENTITY>_<FIELD>_COLUMN`` reports spurious drift —
442
+ the caller reads it from the SAME ``--column-naming`` flag `gen` accepts.
429
443
  """
430
444
  root, errors = _load_root(metadata_dir, strict=strict, providers=providers)
431
445
  if root is None:
@@ -438,7 +452,10 @@ def _generate(
438
452
  # directory deleted seconds later. The real gen paths anchor their own manifest via
439
453
  # gen_state_dir_for().
440
454
  return (
441
- _run_suite(root, out_dir, generators, entity_filter, emit_package_init, None),
455
+ _run_suite(
456
+ root, out_dir, generators, entity_filter, emit_package_init, None,
457
+ column_naming=column_naming,
458
+ ),
442
459
  [],
443
460
  )
444
461
 
@@ -454,6 +471,65 @@ def gen_state_dir_for(metadata_dir: str) -> str:
454
471
  return str(Path(metadata_dir).resolve().parent / ".metaobjects" / ".gen-state")
455
472
 
456
473
 
474
+ #: The conventional declarative-template-spec file (SP-1 §4), discovered when
475
+ #: ``--template-spec`` is not passed. VISIBLE and at the project root on purpose:
476
+ #: this repo splits author input (visible — ``metaobjects/``, ``templates/``) from
477
+ #: tool state (hidden — ``.metaobjects/``), and a spec is authored, not generated.
478
+ #: It also sits beside the ``templates/`` dir its refs resolve under.
479
+ TEMPLATE_SPEC_FILENAME = "template-spec.json"
480
+
481
+
482
+ def project_root_for(metadata_dir: str) -> Path:
483
+ """The project a metadata directory belongs to: its PARENT — the directory
484
+ holding ``metaobjects/``. Same rule as :func:`gen_state_dir_for` (which is
485
+ anchored here) and the C# port's ``GenCommand.ProjectRootFor``."""
486
+ return Path(metadata_dir).resolve().parent
487
+
488
+
489
+ def template_spec_path_for(metadata_dir: str, explicit: str | None) -> str | None:
490
+ """Resolve the template-spec to use, or ``None`` for "no template generators".
491
+
492
+ An explicit ``--template-spec`` always wins and is used verbatim (a flag naming
493
+ a missing file stays a hard error at read time — silently ignoring a path the
494
+ user typed would be worse). Otherwise the conventional
495
+ ``<projectRoot>/template-spec.json`` is used IF it exists.
496
+
497
+ **Every code path that builds a generator list must call this.** The defect this
498
+ exists to prevent is `gen` and `verify --codegen` resolving the spec differently:
499
+ `gen` honoured the flag while `verify` built its own list and never looked, so
500
+ `verify` regenerated WITHOUT the template generators and reported their committed
501
+ output as stale — with a remedy that loops (regenerating cannot produce files the
502
+ regen does not know about).
503
+ """
504
+ if explicit:
505
+ return explicit
506
+ candidate = project_root_for(metadata_dir) / TEMPLATE_SPEC_FILENAME
507
+ return str(candidate) if candidate.is_file() else None
508
+
509
+
510
+ def template_spec_generators(
511
+ metadata_dir: str, explicit: str | None, templates_dir: str,
512
+ ) -> tuple[list[Generator], str | None]:
513
+ """The declarative Mustache generators for this project, or an error message.
514
+
515
+ Returns ``(generators, None)`` on success — an EMPTY list when there is no spec
516
+ at all — and ``([], message)`` when a spec was found but could not be used.
517
+ """
518
+ spec_path = template_spec_path_for(metadata_dir, explicit)
519
+ if spec_path is None:
520
+ return [], None
521
+ from metaobjects.codegen.template_codegen.template_spec import (
522
+ parse_template_spec,
523
+ template_spec_to_generators,
524
+ )
525
+
526
+ try:
527
+ spec = parse_template_spec(json.loads(Path(spec_path).read_text(encoding="utf-8")))
528
+ except (OSError, ValueError) as exc:
529
+ return [], f"error: invalid template-spec {spec_path!r}: {exc}"
530
+ return template_spec_to_generators(spec, FilesystemProvider(templates_dir)), None
531
+
532
+
457
533
  def resolve_metadata_location(
458
534
  config: ProjectConfig | None,
459
535
  root: Path,
@@ -696,20 +772,12 @@ def _cmd_gen(args: argparse.Namespace) -> int:
696
772
  # SEPARATE pass with emit_package_init=False — no Python __init__.py is injected
697
773
  # into their (possibly non-Python) output tree. Templates resolve under
698
774
  # --templates via a FilesystemProvider.
699
- spec_gens: list[Generator] = []
700
- if getattr(args, "template_spec", None):
701
- from metaobjects.codegen.template_codegen.template_spec import (
702
- parse_template_spec,
703
- template_spec_to_generators,
704
- )
705
-
706
- try:
707
- spec = parse_template_spec(json.loads(Path(args.template_spec).read_text(encoding="utf-8")))
708
- except (OSError, ValueError) as exc:
709
- print(f"error: invalid --template-spec: {exc}", file=sys.stderr)
710
- return 1
711
- provider = FilesystemProvider(args.templates)
712
- spec_gens = template_spec_to_generators(spec, provider)
775
+ spec_gens, spec_err = template_spec_generators(
776
+ args.metadata_dir, getattr(args, "template_spec", None), args.templates,
777
+ )
778
+ if spec_err is not None:
779
+ print(spec_err, file=sys.stderr)
780
+ return 1
713
781
 
714
782
  entities = _parse_entities(getattr(args, "entities", None))
715
783
  providers, providers_ok = _providers_from_args(args)
@@ -724,7 +792,11 @@ def _cmd_gen(args: argparse.Namespace) -> int:
724
792
  print(f" {msg}", file=sys.stderr)
725
793
  return 1
726
794
  gen_state = gen_state_dir_for(args.metadata_dir)
727
- written = _run_suite(root, args.out, generators, entities, gen_state_dir=gen_state)
795
+ column_naming = getattr(args, "column_naming", None) or DEFAULT_COLUMN_NAMING
796
+ written = _run_suite(
797
+ root, args.out, generators, entities,
798
+ gen_state_dir=gen_state, column_naming=column_naming,
799
+ )
728
800
  if spec_gens:
729
801
  # The template-spec pass renders user-supplied templates: a bad ref or a
730
802
  # wrong --templates dir raises RenderError (not OSError/ValueError, so it
@@ -734,7 +806,7 @@ def _cmd_gen(args: argparse.Namespace) -> int:
734
806
  try:
735
807
  spec_written = _run_suite(
736
808
  root, args.out, spec_gens, entities, emit_package_init=False,
737
- gen_state_dir=gen_state,
809
+ gen_state_dir=gen_state, column_naming=column_naming,
738
810
  )
739
811
  except RenderError as exc:
740
812
  print(
@@ -845,7 +917,11 @@ def _cmd_gen_neutral_fallback(args: argparse.Namespace) -> int:
845
917
  return 1
846
918
 
847
919
  gen_state = str(root_dir.resolve() / ".metaobjects" / ".gen-state")
848
- written = _run_suite(root, args.out, generators, entities, gen_state_dir=gen_state)
920
+ column_naming = getattr(args, "column_naming", None) or DEFAULT_COLUMN_NAMING
921
+ written = _run_suite(
922
+ root, args.out, generators, entities,
923
+ gen_state_dir=gen_state, column_naming=column_naming,
924
+ )
849
925
  for path in written:
850
926
  print(path)
851
927
  print(f"metaobjects gen: wrote {len(written)} file(s) to {args.out}")
@@ -864,6 +940,23 @@ def _cmd_gen_config(args: argparse.Namespace) -> int:
864
940
  config_path = _find_config(args)
865
941
  if config_path is None:
866
942
  return _cmd_gen_neutral_fallback(args)
943
+ # An EXPLICIT --template-spec here was silently ignored: config mode goes straight
944
+ # to _run_gen_targets, which has no spec pass. Refuse instead of accepting a flag
945
+ # that does nothing. It cannot simply be honoured, because a spec has no `target`
946
+ # (the CLI ports reject that field outright) while config mode is target-shaped, so
947
+ # there is no non-arbitrary outDir to write into. A DISCOVERED spec is ignored here
948
+ # rather than refused — discovery must not hard-fail a command that never asked for
949
+ # it, and gen/verify agree in config mode either way, so no drift can result.
950
+ if getattr(args, "template_spec", None):
951
+ print(
952
+ "error: --template-spec is not supported in declarative-config mode "
953
+ f"({config_path}): a template-spec entry names no target, and config mode "
954
+ "writes per target, so there is no outDir to render into. Run the explicit "
955
+ "form instead: metaobjects gen <metadata_dir> --out <dir> --template-spec "
956
+ "<json> --templates <dir>.",
957
+ file=sys.stderr,
958
+ )
959
+ return 2
867
960
  try:
868
961
  config = load_project_config(config_path)
869
962
  except ConfigError as exc:
@@ -903,33 +996,83 @@ def _cmd_gen_config(args: argparse.Namespace) -> int:
903
996
  return 0
904
997
 
905
998
 
999
+ #: Never compared: interpreter droppings, not generated artifacts. A committed
1000
+ #: ``__pycache__`` would otherwise read as drift on every run.
1001
+ _UNCOMPARED_DIRS = {"__pycache__", ".gen-state", ".metaobjects"}
1002
+ _UNCOMPARED_SUFFIXES = {".pyc", ".pyo"}
1003
+
1004
+
906
1005
  def _relative_set(root: Path) -> dict[str, str]:
907
- """Map every ``*.py`` file under ``root`` to its content, keyed by rel path.
1006
+ """Map every generated-artifact file under ``root`` to its content, keyed by rel path.
908
1007
 
909
- Scoped to ``*.py`` because the Python codegen suite emits only Python sources;
910
- if a generator ever emits a non-``.py`` artifact, broaden this glob so ``verify``
911
- drift-checks it too.
1008
+ Deliberately NOT scoped to ``*.py``. It was, with a note saying to broaden it "if a
1009
+ generator ever emits a non-``.py`` artifact" — and one does: the declarative
1010
+ ``--template-spec`` generators emit text/markdown/csv/json/xml/html by design. The
1011
+ consequence was a gate that answered ``in sync`` with a template-emitted file
1012
+ DELETED and another CORRUPTED, because neither was a ``.py`` and neither was
1013
+ compared. A gate silent about its own output is worse than one that is wrong.
1014
+
1015
+ Binary files are skipped rather than decoded: every generator this project ships
1016
+ emits text, so an undecodable file is by definition not ours.
912
1017
  """
913
1018
  files: dict[str, str] = {}
914
- if root.exists():
915
- for p in sorted(root.rglob("*.py")):
916
- files[str(p.relative_to(root))] = p.read_text()
1019
+ if not root.exists():
1020
+ return files
1021
+ for p in sorted(root.rglob("*")):
1022
+ if not p.is_file():
1023
+ continue
1024
+ rel = p.relative_to(root)
1025
+ if any(part in _UNCOMPARED_DIRS for part in rel.parts):
1026
+ continue
1027
+ if p.suffix in _UNCOMPARED_SUFFIXES:
1028
+ continue
1029
+ try:
1030
+ files[str(rel)] = p.read_text(encoding="utf-8")
1031
+ except (UnicodeDecodeError, OSError):
1032
+ continue
917
1033
  return files
918
1034
 
919
1035
 
920
- def _diff_report(expected: dict[str, str], committed: dict[str, str]) -> int:
1036
+ def _is_ours_for(gen_state_dir: str | None):
1037
+ """Jurisdiction predicate for the ``extra`` verdict: did WE write this path?
1038
+
1039
+ ``out_dir`` is a DIRECTORY, not a namespace this tool owns — the same ruling the
1040
+ TypeScript gate reached (0.24.3), for the same reason. Broadening the comparison
1041
+ beyond ``*.py`` means the gate now sees every stranger's file too, and convicting
1042
+ those turns a project with zero drift into a red build.
1043
+
1044
+ The manifest already records what we WROTE, keyed relative to ``out_dir`` — the
1045
+ same key space this diff uses — so it answers the ownership question exactly.
1046
+ FAILS CLOSED, matching :func:`is_pristine_generated`: with no manifest at all
1047
+ nothing is proven, so every file stays in scope and the old verdict stands.
1048
+ """
1049
+ if gen_state_dir is None or not has_hash_manifest(gen_state_dir):
1050
+ return lambda _rel: True
1051
+ return lambda rel: read_generated_hash(gen_state_dir, rel) is not None
1052
+
1053
+
1054
+ def _diff_report(
1055
+ expected: dict[str, str],
1056
+ committed: dict[str, str],
1057
+ gen_state_dir: str | None = None,
1058
+ ) -> int:
921
1059
  """Compare a regenerated file map against the committed one; print the
922
1060
  standard ``verify --codegen`` drift report. Returns 0 (in sync) or 1.
923
1061
 
924
1062
  Extracted so the explicit-``<metadata_dir>`` flag mode
925
1063
  (:func:`_verify_codegen`) and the ``.metaobjects/config.json`` fallback
926
1064
  rung (:func:`_verify_codegen_neutral_fallback`) report drift identically.
1065
+
1066
+ ``gen_state_dir`` scopes the ``extra`` verdict to files we have a record of
1067
+ writing — see :func:`_is_ours_for`. Omit it to keep the unscoped verdict.
927
1068
  """
1069
+ is_ours = _is_ours_for(gen_state_dir)
928
1070
  changed = sorted(
929
1071
  k for k in expected if k in committed and expected[k] != committed[k]
930
1072
  )
931
1073
  missing = sorted(k for k in expected if k not in committed) # not yet committed
932
- extra = sorted(k for k in committed if k not in expected) # stale committed file
1074
+ # A committed file a fresh regen would not emit is STALE only if it is ours.
1075
+ extra = sorted(k for k in committed if k not in expected and is_ours(k))
933
1076
 
934
1077
  if not changed and not missing and not extra:
935
1078
  print(f"metaobjects verify: in sync ({len(expected)} file(s)).")
@@ -973,13 +1116,37 @@ def _verify_codegen(args: argparse.Namespace) -> int:
973
1116
  # ADR-0023 strict-by-default (#96): verify loads strict unless --lax is given,
974
1117
  # so an undeclared/typo'd own @attr fails verify (matching Java's Maven goal).
975
1118
  strict = not getattr(args, "lax", False)
1119
+ # R31: the `names` generator's constants are strategy-dependent (#248-adjacent —
1120
+ # the same generator Task 9 turned on). A regen that defaults to `literal` while
1121
+ # the committed output was produced with `gen --column-naming snake_case` (or any
1122
+ # other non-default strategy) convicts every <ENTITY>_<FIELD>_COLUMN constant as
1123
+ # drift — a gate failing work the product itself sanctions. Must match the `gen`
1124
+ # invocation that produced --out, exactly like --entities above.
1125
+ column_naming = getattr(args, "column_naming", None) or DEFAULT_COLUMN_NAMING
976
1126
  providers, providers_ok = _providers_from_args(args)
977
1127
  if not providers_ok:
978
1128
  return 1
1129
+ # The declarative template generators, resolved by the SAME rule `gen` uses —
1130
+ # explicit flag, else the conventional <projectRoot>/template-spec.json. Without
1131
+ # this, verify regenerated only the default suite and then reported every
1132
+ # spec-emitted file as `extra:` ("stale committed file"), exiting 1 with a remedy
1133
+ # that loops: `gen` reproduces those files but the next verify fails identically.
1134
+ # `verify` spells the templates dir --templates-root (its --templates is the
1135
+ # subverb selector), so the flag is read per-verb and threaded to one resolver.
1136
+ spec_gens, spec_err = template_spec_generators(
1137
+ args.metadata_dir,
1138
+ getattr(args, "template_spec", None),
1139
+ getattr(args, "templates_root", None) or "templates",
1140
+ )
1141
+ if spec_err is not None:
1142
+ print(spec_err, file=sys.stderr)
1143
+ return 1
1144
+
979
1145
  with tempfile.TemporaryDirectory() as tmp:
980
1146
  entities = _parse_entities(getattr(args, "entities", None))
981
1147
  written, errors = _generate(
982
- args.metadata_dir, tmp, None, entities, strict=strict, providers=providers
1148
+ args.metadata_dir, tmp, None, entities, strict=strict, providers=providers,
1149
+ column_naming=column_naming,
983
1150
  )
984
1151
  if errors:
985
1152
  print("error: failed to load metadata:", file=sys.stderr)
@@ -989,10 +1156,30 @@ def _verify_codegen(args: argparse.Namespace) -> int:
989
1156
  print(_strict_load_hint(), file=sys.stderr)
990
1157
  return 1
991
1158
 
1159
+ if spec_gens:
1160
+ # Mirror `gen`'s SECOND pass exactly, emit_package_init=False included —
1161
+ # a diff against output produced by a two-pass gen has to be produced the
1162
+ # same way, or the __init__.py placement alone reads as drift.
1163
+ from metaobjects.render.renderer import RenderError
1164
+
1165
+ try:
1166
+ _generate(
1167
+ args.metadata_dir, tmp, spec_gens, entities,
1168
+ emit_package_init=False, strict=strict, providers=providers,
1169
+ column_naming=column_naming,
1170
+ )
1171
+ except RenderError as exc:
1172
+ print(
1173
+ f"error: template-spec render failed (check the template refs and "
1174
+ f"the templates dir): {exc}",
1175
+ file=sys.stderr,
1176
+ )
1177
+ return 1
1178
+
992
1179
  expected = _relative_set(Path(tmp))
993
1180
  committed = _relative_set(Path(args.out))
994
1181
 
995
- return _diff_report(expected, committed)
1182
+ return _diff_report(expected, committed, gen_state_dir_for(args.metadata_dir))
996
1183
 
997
1184
 
998
1185
  def _verify_codegen_neutral_fallback(args: argparse.Namespace) -> int:
@@ -1018,6 +1205,10 @@ def _verify_codegen_neutral_fallback(args: argparse.Namespace) -> int:
1018
1205
  return 1
1019
1206
 
1020
1207
  strict = not getattr(args, "lax", False)
1208
+ # R31 — see _verify_codegen: must match the strategy `gen` (this same rung)
1209
+ # produced --out with, or the `names` generator's constants regenerate under
1210
+ # the wrong strategy and report spurious drift.
1211
+ column_naming = getattr(args, "column_naming", None) or DEFAULT_COLUMN_NAMING
1021
1212
  providers, providers_ok = _providers_from_args(args)
1022
1213
  if not providers_ok:
1023
1214
  return 1
@@ -1033,11 +1224,18 @@ def _verify_codegen_neutral_fallback(args: argparse.Namespace) -> int:
1033
1224
  print(_strict_load_hint(), file=sys.stderr)
1034
1225
  return 1
1035
1226
 
1036
- _run_suite(root, tmp, None, entities, gen_state_dir=None)
1227
+ _run_suite(
1228
+ root, tmp, None, entities, gen_state_dir=None, column_naming=column_naming
1229
+ )
1037
1230
  expected = _relative_set(Path(tmp))
1038
1231
  committed = _relative_set(Path(args.out))
1039
1232
 
1040
- return _diff_report(expected, committed)
1233
+ # This rung has NO positional metadata dir — cwd is the project (same anchor
1234
+ # `_cmd_gen_neutral_fallback` writes its manifest to), so the jurisdiction lookup
1235
+ # must be built from that, not from args.metadata_dir (None here by definition).
1236
+ return _diff_report(
1237
+ expected, committed, str(root_dir.resolve() / ".metaobjects" / ".gen-state")
1238
+ )
1041
1239
 
1042
1240
 
1043
1241
  def _temp_slot_for(temp_root: Path, real_outdir: str, config_dir: Path) -> str:
@@ -1150,7 +1348,11 @@ def _verify_codegen_config(args: argparse.Namespace) -> int:
1150
1348
  k for k in expected if k in committed and expected[k] != committed[k]
1151
1349
  )
1152
1350
  missing = sorted(k for k in expected if k not in committed)
1153
- extra = sorted(k for k in committed if k not in expected)
1351
+ # Same jurisdiction rule as _diff_report — this rung inlines its diff to
1352
+ # report per-target, so the guard has to be inlined with it. Without it,
1353
+ # broadening the comparison past *.py convicts every stranger's file.
1354
+ is_ours = _is_ours_for(gen_state_dir_for(config.metadata_dir()))
1355
+ extra = sorted(k for k in committed if k not in expected and is_ours(k))
1154
1356
 
1155
1357
  names = "+".join(
1156
1358
  t.name for t in closure if config.out_dir_for(t) == real_outdir
@@ -1411,6 +1613,19 @@ def _build_parser() -> argparse.ArgumentParser:
1411
1613
  help="directory of metadata JSON/YAML files",
1412
1614
  )
1413
1615
  gen.add_argument("--out", default=None, help="output directory for generated code")
1616
+ gen.add_argument(
1617
+ "--column-naming",
1618
+ dest="column_naming",
1619
+ choices=COLUMN_NAMING_STRATEGIES,
1620
+ default=DEFAULT_COLUMN_NAMING,
1621
+ help=(
1622
+ "column-naming strategy for a field with no explicit @column "
1623
+ f"(default: {DEFAULT_COLUMN_NAMING!r}, matching this port's "
1624
+ "ObjectManager default — NOT `meta migrate`'s snake_case). Read by "
1625
+ "the `names` generator; pass the same strategy the schema was "
1626
+ "created with."
1627
+ ),
1628
+ )
1414
1629
  gen.add_argument(
1415
1630
  "--generators",
1416
1631
  default=None,
@@ -1550,6 +1765,19 @@ def _build_parser() -> argparse.ArgumentParser:
1550
1765
  action="store_true",
1551
1766
  help="codegen drift: regenerate to a temp dir + diff vs --out (default)",
1552
1767
  )
1768
+ verify.add_argument(
1769
+ "--column-naming",
1770
+ dest="column_naming",
1771
+ choices=COLUMN_NAMING_STRATEGIES,
1772
+ default=DEFAULT_COLUMN_NAMING,
1773
+ help=(
1774
+ "column-naming strategy the committed --out was generated with (default: "
1775
+ f"{DEFAULT_COLUMN_NAMING!r}, matching `gen`'s default). The `names` "
1776
+ "generator's constants are strategy-dependent, so a --codegen regen must "
1777
+ "use the SAME strategy `gen` was run with or every <ENTITY>_<FIELD>_COLUMN "
1778
+ "constant reports spurious drift."
1779
+ ),
1780
+ )
1553
1781
  verify.add_argument(
1554
1782
  "--templates",
1555
1783
  action="store_true",