metaobjects 1.0.8rc1__tar.gz → 1.0.9__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 (477) hide show
  1. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/PKG-INFO +1 -1
  2. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/pyproject.toml +1 -1
  3. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/cli.py +78 -10
  4. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/eject.py +76 -3
  5. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generator_registry.py +14 -3
  6. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/extractor_generator.py +14 -7
  7. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/router_generator.py +66 -7
  8. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/trace_helper_generator.py +11 -6
  9. metaobjects-1.0.9/src/metaobjects/codegen/model_walk.py +101 -0
  10. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/runner.py +5 -1
  11. metaobjects-1.0.9/src/metaobjects/codegen/runtime/__init__.py +9 -0
  12. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/object/object_extract.py +25 -10
  13. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/extract.py +16 -6
  14. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/json_forgiving_reader.py +39 -3
  15. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/locate.py +14 -2
  16. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/types.py +18 -0
  17. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/prompt/output_format_renderer.py +14 -2
  18. metaobjects-1.0.9/tests/codegen/test_cli_gen_unknown_attr.py +70 -0
  19. metaobjects-1.0.9/tests/codegen/test_eject_runtime.py +331 -0
  20. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_extractor_generator.py +29 -1
  21. metaobjects-1.0.9/tests/codegen/test_recipe_generators.py +105 -0
  22. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_trace_helper_generator.py +3 -1
  23. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_extract_conformance.py +6 -1
  24. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_output_prompt_conformance.py +1 -1
  25. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/uv.lock +1 -1
  26. metaobjects-1.0.8rc1/src/metaobjects/codegen/runtime/__init__.py +0 -6
  27. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/.gitignore +0 -0
  28. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/LICENSE +0 -0
  29. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/README.md +0 -0
  30. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/hatch_build.py +0 -0
  31. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/scripts/generate_embedded_library.py +0 -0
  32. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/__init__.py +0 -0
  33. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/agent_context/__init__.py +0 -0
  34. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/agent_context/scaffold.py +0 -0
  35. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/apidocs/__init__.py +0 -0
  36. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/apidocs/api_model.py +0 -0
  37. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/apidocs/builder.py +0 -0
  38. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/apidocs/naming.py +0 -0
  39. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/apidocs/paths.py +0 -0
  40. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/apidocs/renderer.py +0 -0
  41. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/attr_class_map.py +0 -0
  42. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  43. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/__init__.py +0 -0
  44. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/collision_names.py +0 -0
  45. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/config.py +0 -0
  46. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/constants.py +0 -0
  47. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  48. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  49. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/format.py +0 -0
  50. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  51. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generator.py +0 -0
  52. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/__init__.py +0 -0
  53. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  54. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  55. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/find_inbound.py +0 -0
  56. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  57. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  58. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/names_generator.py +0 -0
  59. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  60. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  61. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  62. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  63. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  64. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  65. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/metaobjects-config.schema.json +0 -0
  66. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  67. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  68. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/project_config.py +0 -0
  69. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/runtime/constraint_errors.py +0 -0
  70. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  71. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  72. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  73. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  74. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  75. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/type_map.py +0 -0
  76. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/codegen/value_objects.py +0 -0
  77. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/config/__init__.py +0 -0
  78. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/config/dependencies.py +0 -0
  79. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/config/neutral_config.py +0 -0
  80. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/config/source_resolver.py +0 -0
  81. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/core_types.py +0 -0
  82. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/datatype.py +0 -0
  83. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/documentation/__init__.py +0 -0
  84. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/documentation/doc_constants.py +0 -0
  85. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/documentation/doc_provider.py +0 -0
  86. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/documentation/doc_schema.py +0 -0
  87. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/errors.py +0 -0
  88. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/library/__init__.py +0 -0
  89. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/library/embedded_library.py +0 -0
  90. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/library/library_sources.py +0 -0
  91. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/__init__.py +0 -0
  92. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/merge.py +0 -0
  93. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/meta_data_loader.py +0 -0
  94. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/registered_validation.py +0 -0
  95. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/sources/__init__.py +0 -0
  96. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/sources/directory_source.py +0 -0
  97. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/sources/file_source.py +0 -0
  98. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  99. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/sources/uri_source.py +0 -0
  100. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/validate_discriminator.py +0 -0
  101. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
  102. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/validate_field_mutability.py +0 -0
  103. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  104. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  105. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  106. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/loader/validation_passes.py +0 -0
  107. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/__init__.py +0 -0
  108. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/__init__.py +0 -0
  109. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  110. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  111. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  112. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/field/__init__.py +0 -0
  113. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  114. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  115. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  116. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  117. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  118. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/index/__init__.py +0 -0
  119. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  120. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  121. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/object/__init__.py +0 -0
  122. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  123. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  124. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  125. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  126. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/object/value_object.py +0 -0
  127. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  128. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  129. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  130. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  131. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/relationship/relationship_references.py +0 -0
  132. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
  133. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/requirement/meta_requirement.py +0 -0
  134. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/requirement/requirement_constants.py +0 -0
  135. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  136. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  137. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/meta_data.py +0 -0
  138. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/meta_root.py +0 -0
  139. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/__init__.py +0 -0
  140. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  141. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  142. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  143. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  144. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  145. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  146. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  147. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  148. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  149. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/__init__.py +0 -0
  150. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  151. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  152. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  153. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  154. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  155. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  156. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  157. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  158. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/provider_extends.py +0 -0
  159. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/template/__init__.py +0 -0
  160. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/template/meta_template.py +0 -0
  161. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  162. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta/template/template_constants.py +0 -0
  163. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/meta_endpoint.py +0 -0
  164. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/naming.py +0 -0
  165. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/naming_refs.py +0 -0
  166. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/parser.py +0 -0
  167. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/parser_yaml.py +0 -0
  168. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/provider.py +0 -0
  169. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/py.typed +0 -0
  170. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/registry.py +0 -0
  171. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/registry_manifest.py +0 -0
  172. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/__init__.py +0 -0
  173. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/email_document.py +0 -0
  174. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/escapers.py +0 -0
  175. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  176. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/__init__.py +0 -0
  177. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/coerce.py +0 -0
  178. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/extract_map.py +0 -0
  179. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/normalize.py +0 -0
  180. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/strip.py +0 -0
  181. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  182. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/filesystem_provider.py +0 -0
  183. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/payload_accessors.py +0 -0
  184. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/prompt/__init__.py +0 -0
  185. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  186. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  187. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  188. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  189. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/renderer.py +0 -0
  190. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/render/verify.py +0 -0
  191. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/runtime/__init__.py +0 -0
  192. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/runtime/llm_recorder.py +0 -0
  193. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  194. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/runtime/object_manager.py +0 -0
  195. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/runtime/tph.py +0 -0
  196. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/scope.py +0 -0
  197. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/serializer_json.py +0 -0
  198. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/shared/__init__.py +0 -0
  199. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/shared/base_types.py +0 -0
  200. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/shared/separators.py +0 -0
  201. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/shared/structural.py +0 -0
  202. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/source/__init__.py +0 -0
  203. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/source/error_source.py +0 -0
  204. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/source/json_path.py +0 -0
  205. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/source/semantic_diff.py +0 -0
  206. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/source/yaml_positions.py +0 -0
  207. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/source_resolution.py +0 -0
  208. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  209. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/attr.json +0 -0
  210. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/db.json +0 -0
  211. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  212. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/field.json +0 -0
  213. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/identity.json +0 -0
  214. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/index.json +0 -0
  215. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/layout.json +0 -0
  216. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/object.json +0 -0
  217. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/origin.json +0 -0
  218. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  219. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  220. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/requirement.json +0 -0
  221. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/source.json +0 -0
  222. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/template.json +0 -0
  223. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  224. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/ui.json +0 -0
  225. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/validator.json +0 -0
  226. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/spec_metamodel/view.json +0 -0
  227. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/super_resolve.py +0 -0
  228. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/validation_types.py +0 -0
  229. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/src/metaobjects/yaml_desugar.py +0 -0
  230. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/__init__.py +0 -0
  231. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/__init__.py +0 -0
  232. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/gen_suite.py +0 -0
  233. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  234. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/extends/expected/Program.py +0 -0
  235. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/extends/meta.json +0 -0
  236. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  237. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  238. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/nested-array/meta.json +0 -0
  239. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  240. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  241. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/scalars/meta.json +0 -0
  242. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  243. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/golden/vanilla/meta.json +0 -0
  244. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_abstract_conformance.py +0 -0
  245. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_api_docs_builder.py +0 -0
  246. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_api_docs_paths.py +0 -0
  247. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli.py +0 -0
  248. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_config_gen.py +0 -0
  249. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_config_verify.py +0 -0
  250. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_dependencies.py +0 -0
  251. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_providers.py +0 -0
  252. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_registry.py +0 -0
  253. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_render_helper_template_root.py +0 -0
  254. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  255. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_verify_strict.py +0 -0
  256. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  257. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_codegen_compile_conformance.py +0 -0
  258. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_constants_config.py +0 -0
  259. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_eject.py +0 -0
  260. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_entity_model.py +0 -0
  261. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_enum_conformance.py +0 -0
  262. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  263. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_extract_tier_collision.py +0 -0
  264. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  265. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_format.py +0 -0
  266. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  267. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_fr010_output_codegen.py +0 -0
  268. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  269. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_gen_state_keying.py +0 -0
  270. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_generator.py +0 -0
  271. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_generator_extension_seams.py +0 -0
  272. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_golden.py +0 -0
  273. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  274. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_inheritance_conformance.py +0 -0
  275. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_instance_artifacts.py +0 -0
  276. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_m2m_codegen.py +0 -0
  277. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_names_extends_chain.py +0 -0
  278. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_names_generator.py +0 -0
  279. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_no_magic_physical_names.py +0 -0
  280. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_origin_nullability.py +0 -0
  281. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_output_parser_generator.py +0 -0
  282. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_overwrite_policy.py +0 -0
  283. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_overwrite_policy_hash.py +0 -0
  284. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_project_config.py +0 -0
  285. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_projection_compile.py +0 -0
  286. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_render_helper_conformance.py +0 -0
  287. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_render_helper_generator.py +0 -0
  288. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_reverse_finders.py +0 -0
  289. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_route_path_naming.py +0 -0
  290. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_router_autoset.py +0 -0
  291. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_router_generator.py +0 -0
  292. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_router_generator_m2m_target_tph.py +0 -0
  293. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_router_generator_tph_m2m.py +0 -0
  294. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_router_generator_tph_m2m_shadowed.py +0 -0
  295. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_runner.py +0 -0
  296. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_source_resolution.py +0 -0
  297. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_sourceless_projection.py +0 -0
  298. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_template_data.py +0 -0
  299. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_template_generator.py +0 -0
  300. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_template_output_pattern.py +0 -0
  301. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_template_scope_helpers.py +0 -0
  302. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_template_scope_walk.py +0 -0
  303. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_template_spec.py +0 -0
  304. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_tph_codegen.py +0 -0
  305. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_type_map.py +0 -0
  306. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_validation_conformance.py +0 -0
  307. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_value_objects.py +0 -0
  308. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/codegen/test_write_through.py +0 -0
  309. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/config/__init__.py +0 -0
  310. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/config/test_dependencies.py +0 -0
  311. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/config/test_library_package_guard.py +0 -0
  312. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/config/test_neutral_config.py +0 -0
  313. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/config/test_source_resolver.py +0 -0
  314. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/__init__.py +0 -0
  315. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/capabilities.py +0 -0
  316. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/conformance-expected-failures.json +0 -0
  317. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/conformance_adapter.py +0 -0
  318. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/corpus.py +0 -0
  319. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/expected_failures.py +0 -0
  320. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/fixture_discovery.py +0 -0
  321. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/navigator.py +0 -0
  322. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  323. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_conformance.py +0 -0
  324. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_dependency_conformance.py +0 -0
  325. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_extract_object_verdict.py +0 -0
  326. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  327. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_fr011_attrs.py +0 -0
  328. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_generator_registry_conformance.py +0 -0
  329. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_object_model_conformance.py +0 -0
  330. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_provider_composition_conformance.py +0 -0
  331. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_registry_conformance.py +0 -0
  332. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_runner_hardfail.py +0 -0
  333. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_scope_conformance.py +0 -0
  334. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_source_resolution_conformance.py +0 -0
  335. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  336. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_strict_attr_load.py +0 -0
  337. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_template_codegen_conformance.py +0 -0
  338. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_template_generator_conformance.py +0 -0
  339. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/test_yaml_conformance.py +0 -0
  340. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  341. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/__init__.py +0 -0
  342. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/api_contract_assertions.py +0 -0
  343. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/api_contract_jsonb_server.py +0 -0
  344. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/api_contract_m2m_server.py +0 -0
  345. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/api_contract_server.py +0 -0
  346. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/generated_jsonb_app.py +0 -0
  347. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/generated_m2m_app.py +0 -0
  348. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/generated_projection_app.py +0 -0
  349. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/generated_router_app.py +0 -0
  350. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/generated_tph_app.py +0 -0
  351. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/meta_ai_trace.yaml +0 -0
  352. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/normalization.py +0 -0
  353. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/postgres_container.py +0 -0
  354. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/query_runner.py +0 -0
  355. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/scenarios.py +0 -0
  356. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_api_contract.py +0 -0
  357. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_api_contract_generated.py +0 -0
  358. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_api_contract_jsonb.py +0 -0
  359. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_api_contract_m2m.py +0 -0
  360. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  361. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_api_contract_projection.py +0 -0
  362. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_api_contract_tph_generated.py +0 -0
  363. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  364. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_llm_call_trace.py +0 -0
  365. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_m2m_target_tph_generated.py +0 -0
  366. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_normalization.py +0 -0
  367. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_query_scenarios.py +0 -0
  368. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_runtime_return_types.py +0 -0
  369. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_tph_m2m_generated.py +0 -0
  370. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/integration/test_tph_m2m_shadowed_generated.py +0 -0
  371. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/loader/__init__.py +0 -0
  372. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/loader/test_abstract_subtype_authored.py +0 -0
  373. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/loader/test_child_wrapper_body_shape.py +0 -0
  374. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/loader/test_enum_extends_values_conflict.py +0 -0
  375. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/loader/test_field_mutability_order.py +0 -0
  376. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/loader/test_shipped_library_ai.py +0 -0
  377. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/loader/test_template_payload_target_210.py +0 -0
  378. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/open_closed_proof_test.py +0 -0
  379. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/__init__.py +0 -0
  380. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/__init__.py +0 -0
  381. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_coerce.py +0 -0
  382. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_extract.py +0 -0
  383. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_extract_map.py +0 -0
  384. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  385. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_locate.py +0 -0
  386. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_model.py +0 -0
  387. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_normalize.py +0 -0
  388. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_strip.py +0 -0
  389. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  390. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/prompt/__init__.py +0 -0
  391. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/prompt/test_output_format_renderer.py +0 -0
  392. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_email_document.py +0 -0
  393. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_filesystem_provider.py +0 -0
  394. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_output_format_renderer_nested.py +0 -0
  395. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_render_conformance.py +0 -0
  396. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_render_max_chars.py +0 -0
  397. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_render_model_payload.py +0 -0
  398. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_verify.py +0 -0
  399. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/render/test_verify_conformance.py +0 -0
  400. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/runtime/test_constraint_errors.py +0 -0
  401. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/runtime/test_object_manager_currency_read.py +0 -0
  402. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/runtime/test_object_manager_enum_intvaluemap.py +0 -0
  403. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/runtime/test_object_manager_table_name.py +0 -0
  404. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/source/__init__.py +0 -0
  405. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/source/test_fr5c_merge_attribution.py +0 -0
  406. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/source/test_fr5d_reference_resolution.py +0 -0
  407. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/source/test_fr5e_database_source_shape.py +0 -0
  408. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/source/test_json_path.py +0 -0
  409. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/source/test_semantic_diff.py +0 -0
  410. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/source/test_source_on_node.py +0 -0
  411. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/source/test_yaml_positions.py +0 -0
  412. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/test_api_docs_accuracy.py +0 -0
  413. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/test_meta_endpoint.py +0 -0
  414. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/__init__.py +0 -0
  415. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_agent_context_staleness.py +0 -0
  416. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_capabilities.py +0 -0
  417. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_column_naming_strategy.py +0 -0
  418. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_common_attrs.py +0 -0
  419. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_core_types.py +0 -0
  420. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_derive_m2m_declaring_entity.py +0 -0
  421. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_effective_children_sibling_shadowing.py +0 -0
  422. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_effective_package.py +0 -0
  423. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_errors.py +0 -0
  424. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_field_enum.py +0 -0
  425. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_field_enum_intvaluemap.py +0 -0
  426. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_field_map_validation.py +0 -0
  427. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  428. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_file_source_id.py +0 -0
  429. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  430. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_index_lookup.py +0 -0
  431. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_llm_recorder.py +0 -0
  432. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_loader.py +0 -0
  433. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_loader_bom.py +0 -0
  434. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_loader_class.py +0 -0
  435. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_merge.py +0 -0
  436. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_meta_attr.py +0 -0
  437. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_meta_data.py +0 -0
  438. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_meta_source.py +0 -0
  439. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_module_shortcuts.py +0 -0
  440. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_n2m_resolver.py +0 -0
  441. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  442. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_no_dead_config_fields.py +0 -0
  443. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  444. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  445. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_object_manager_write_through.py +0 -0
  446. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_one_primary_source.py +0 -0
  447. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_parser.py +0 -0
  448. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_parser_yaml.py +0 -0
  449. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_provider.py +0 -0
  450. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_provider_extension.py +0 -0
  451. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_registry.py +0 -0
  452. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_registry_completeness.py +0 -0
  453. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_registry_extend.py +0 -0
  454. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_registry_sealed.py +0 -0
  455. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_relationship_m2m_validation.py +0 -0
  456. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_relationship_references.py +0 -0
  457. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_relationship_referential_actions.py +0 -0
  458. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_resolution_key.py +0 -0
  459. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  460. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_serializer.py +0 -0
  461. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_serializer_shared_document.py +0 -0
  462. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_shared_constants.py +0 -0
  463. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_smoke.py +0 -0
  464. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_source_escapes.py +0 -0
  465. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_sources.py +0 -0
  466. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_strict_child_placement.py +0 -0
  467. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_super_resolve.py +0 -0
  468. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_template_toolcall.py +0 -0
  469. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  470. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_validation_attr_schema.py +0 -0
  471. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_validation_filter_values.py +0 -0
  472. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_validation_origin_195.py +0 -0
  473. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_validation_origin_paths.py +0 -0
  474. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_validation_sort_field.py +0 -0
  475. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_validation_warnings.py +0 -0
  476. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_write_coercion_arrays.py +0 -0
  477. {metaobjects-1.0.8rc1 → metaobjects-1.0.9}/tests/unit/test_yaml_desugar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: metaobjects
3
- Version: 1.0.8rc1
3
+ Version: 1.0.9
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 = "1.0.8rc1"
3
+ version = "1.0.9"
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"
@@ -57,7 +57,7 @@ from pathlib import Path
57
57
  from typing import Callable
58
58
 
59
59
  from metaobjects import MetaDataLoader
60
- from metaobjects.errors import ParseError
60
+ from metaobjects.errors import ErrorCode, MetaError, ParseError
61
61
  from metaobjects.config.dependencies import (
62
62
  Collection,
63
63
  imported_from,
@@ -276,9 +276,14 @@ def _load_root(
276
276
  strict: bool = False,
277
277
  providers: list[object] | None = None,
278
278
  libraries: list[str] | None = None,
279
+ unknown_attr_out: list[str] | None = None,
279
280
  ) -> tuple[MetaData | None, list[str]]:
280
281
  """Load metadata; return ``(root, error_messages)``. ``root`` is None on error.
281
282
 
283
+ ``unknown_attr_out`` — when given on a LENIENT load, a second strict load of the
284
+ same metadata is run and its ``ERR_UNKNOWN_ATTR`` findings are appended, formatted,
285
+ so ``gen`` can warn about what ``verify`` would reject instead of staying silent.
286
+
282
287
  ``strict`` (ADR-0023, #96) — when True, an undeclared own ``@attr`` →
283
288
  ``ERR_UNKNOWN_ATTR``. Defaults False so ``gen`` / ``docs`` keep the legacy
284
289
  open-attr load; only ``verify`` opts in (strict-by-default with a ``--lax``
@@ -312,20 +317,24 @@ def _load_root(
312
317
  return None, [f"{exc.code}: {exc}"]
313
318
  all_libraries = _merge_libraries(neutral.libraries if neutral is not None else None, libraries)
314
319
 
315
- try:
320
+ def load(strict_load: bool) -> LoadResult:
316
321
  if providers:
317
322
  from metaobjects.core_types import core_providers
318
323
 
319
- result = MetaDataLoader.from_directory(
324
+ return MetaDataLoader.from_directory(
320
325
  metadata_dir,
321
326
  providers=[*core_providers, *providers],
322
- strict=strict,
327
+ strict=strict_load,
323
328
  libraries=all_libraries,
324
329
  )
325
- else:
326
- result = MetaDataLoader.from_directory(
327
- metadata_dir, strict=strict, libraries=all_libraries
328
- )
330
+ return MetaDataLoader.from_directory(
331
+ metadata_dir, strict=strict_load, libraries=all_libraries
332
+ )
333
+
334
+ try:
335
+ result = load(strict)
336
+ if unknown_attr_out is not None and not strict and not result.errors:
337
+ unknown_attr_out.extend(_unknown_attr_findings(load(True)))
329
338
  except OSError as e:
330
339
  # `DirectorySource.expand()` walks via `iterdir()` (not `rglob()`, so it
331
340
  # can follow a symlinked subdirectory — the I1 fix) and raises a plain
@@ -416,6 +425,7 @@ def _load_root_from_collection(
416
425
  strict: bool = False,
417
426
  providers: list[object] | None = None,
418
427
  libraries: list[str] | None = None,
428
+ unknown_attr_out: list[str] | None = None,
419
429
  ) -> tuple[MetaData | None, list[str]]:
420
430
  """Load a resolved `Collection` — the source-resolution ladder's
421
431
  ``.metaobjects/config.json`` rung (:func:`resolve_metadata_location`), which
@@ -432,9 +442,58 @@ def _load_root_from_collection(
432
442
  if result.errors:
433
443
  msgs = [f"{e.code}: {e.message}" for e in result.errors]
434
444
  return None, msgs
445
+ if unknown_attr_out is not None and not strict:
446
+ # See `_load_root`: what a strict load (`verify`) would reject, for `gen` to warn.
447
+ try:
448
+ strict_result = _load_collection_result(
449
+ collection, strict=True, providers=providers, libraries=libraries
450
+ )
451
+ except ParseError:
452
+ strict_result = None
453
+ if strict_result is not None:
454
+ unknown_attr_out.extend(_unknown_attr_findings(strict_result))
435
455
  return result.root, []
436
456
 
437
457
 
458
+ #: Trailing advice after ``gen``'s unknown-attribute warnings. ``gen`` has no strict
459
+ #: flag of its own; ``verify`` is the strict door (ADR-0023), so that is what it names.
460
+ UNKNOWN_ATTR_GEN_ADVICE = (
461
+ "metaobjects gen loads leniently and generated anyway, but `metaobjects verify` "
462
+ "rejects this metadata (ADR-0023): fix or remove the attribute - a typo'd one "
463
+ "(`isAbstrakt`, `requird`) silently changes what is generated."
464
+ )
465
+
466
+
467
+ def _format_unknown_attr(err: MetaError) -> str:
468
+ """One ``gen`` warning: code + message (naming attribute and node), then the file."""
469
+ env = getattr(err, "envelope", None)
470
+ files = [f for f in (getattr(env, "files", None) or ()) if f]
471
+ json_path = getattr(env, "json_path", None)
472
+ where = " ".join(
473
+ p for p in (", ".join(files) if files else None, f"at {json_path}" if json_path else None) if p
474
+ )
475
+ head = f"{err.code.value if hasattr(err.code, 'value') else err.code}: {err.message}"
476
+ return f"{head}\n in {where}" if where else head
477
+
478
+
479
+ def _unknown_attr_findings(result: LoadResult) -> list[str]:
480
+ """The ERR_UNKNOWN_ATTR findings of a STRICT load, formatted for ``gen``."""
481
+ return [
482
+ _format_unknown_attr(e) for e in result.errors
483
+ if (e.code.value if hasattr(e.code, "value") else e.code) == ErrorCode.ERR_UNKNOWN_ATTR.value
484
+ ]
485
+
486
+
487
+ def _print_unknown_attr_warnings(findings: list[str]) -> None:
488
+ """``gen`` loads leniently; an unknown attribute ``verify`` would reject is named here
489
+ as a warning (attribute, node, file). Advisory only: the exit code is unchanged."""
490
+ if not findings:
491
+ return
492
+ for f in findings:
493
+ print(f"warning: {f}", file=sys.stderr)
494
+ print(f"warning: {UNKNOWN_ATTR_GEN_ADVICE}", file=sys.stderr)
495
+
496
+
438
497
  def _strict_load_hint() -> str:
439
498
  """Actionable next-steps when strict verify rejects an undeclared @attr."""
440
499
  return (
@@ -1005,6 +1064,8 @@ def _cmd_list(_args: argparse.Namespace) -> int:
1005
1064
  status = owned.owned_status(root, entry)
1006
1065
  mark = f" [owned — {status}]" if status is not None else ""
1007
1066
  print(f"{entry.name} — {entry.description}{requires}{mark}")
1067
+ for path, status in owned.runtime_status(root):
1068
+ print(f"runtime {path.as_posix()} [owned — {status}]")
1008
1069
  return 0
1009
1070
 
1010
1071
 
@@ -1097,12 +1158,16 @@ def _cmd_gen(args: argparse.Namespace) -> int:
1097
1158
  return 1
1098
1159
  # Load the metadata ONCE; both the default suite and the (optional)
1099
1160
  # --template-spec pass run against this single loaded root.
1100
- root, load_errors = _load_root(args.metadata_dir, providers=providers)
1161
+ unknown_attrs: list[str] = []
1162
+ root, load_errors = _load_root(
1163
+ args.metadata_dir, providers=providers, unknown_attr_out=unknown_attrs
1164
+ )
1101
1165
  if root is None:
1102
1166
  print("error: failed to load metadata:", file=sys.stderr)
1103
1167
  for msg in load_errors:
1104
1168
  print(f" {msg}", file=sys.stderr)
1105
1169
  return 1
1170
+ _print_unknown_attr_warnings(unknown_attrs)
1106
1171
  gen_state = gen_state_dir_for(args.metadata_dir)
1107
1172
  # Same anchor the manifest itself uses. Both passes below key against it, so a
1108
1173
  # `--template-spec` artifact and a default-suite artifact land in one key space.
@@ -1366,14 +1431,17 @@ def _cmd_gen_config(args: argparse.Namespace) -> int:
1366
1431
  if collection is None:
1367
1432
  return 1
1368
1433
 
1434
+ unknown_attrs: list[str] = []
1369
1435
  root, load_errors = _load_root_from_collection(
1370
- collection, providers=providers, libraries=config.libraries
1436
+ collection, providers=providers, libraries=config.libraries,
1437
+ unknown_attr_out=unknown_attrs,
1371
1438
  )
1372
1439
  if root is None:
1373
1440
  print("error: failed to load metadata:", file=sys.stderr)
1374
1441
  for msg in load_errors:
1375
1442
  print(f" {msg}", file=sys.stderr)
1376
1443
  return 1
1444
+ _print_unknown_attr_warnings(unknown_attrs)
1377
1445
 
1378
1446
  for t in targets:
1379
1447
  imported_refusal = _refuse_imported_entities(t.entities, root, collection)
@@ -9,6 +9,14 @@ to wire instead.
9
9
 
10
10
  A copy imports the same ``metaobjects.codegen.*`` module paths the packaged generator
11
11
  does. Those module paths are the documented surface an owned generator builds on.
12
+
13
+ What the copy's OUTPUT imports is a different question. A generator whose generated code
14
+ imports helper runtime (``routes``: ``filter_parser``, ``constraint_errors``) lists it on
15
+ its registry entry, and eject hands that over too: the runtime source lands in
16
+ ``codegen/runtime/`` and the copy's ``OWNED_RUNTIME`` switch is flipped, so the owned
17
+ generator emits that source into the generated package and its output imports the local
18
+ copy. Core modules (loader, registry, render, extract, ``ObjectManager``) stay package
19
+ imports. An existing runtime copy is never overwritten, even with ``--force``.
12
20
  """
13
21
  from __future__ import annotations
14
22
 
@@ -30,6 +38,14 @@ from metaobjects.codegen.generator_registry import (
30
38
  #: Where an owned copy lands, relative to the project root. Mirrors the TS layout.
31
39
  OWNED_DIR = Path("codegen") / "generators"
32
40
 
41
+ #: Where the helper runtime an owned generator's output depends on lands. A sibling of
42
+ #: :data:`OWNED_DIR`, which is where the owned router reads it from.
43
+ RUNTIME_DIR = Path("codegen") / "runtime"
44
+
45
+ #: The switch an owned copy flips to emit and import its runtime locally.
46
+ _RUNTIME_SWITCH_OFF = "OWNED_RUNTIME = False"
47
+ _RUNTIME_SWITCH_ON = "OWNED_RUNTIME = True"
48
+
33
49
 
34
50
  def owned_stem(name: str) -> str:
35
51
  """The module name of an owned copy: the stable name with ``-`` as ``_``."""
@@ -52,6 +68,32 @@ def packaged_source(entry: GeneratorEntry) -> str:
52
68
  return Path(module.__file__).read_text(encoding="utf-8")
53
69
 
54
70
 
71
+ def ejected_source(entry: GeneratorEntry) -> str:
72
+ """What ``eject`` writes for *entry*: the packaged source, with the runtime switch on
73
+ when its output imports helper runtime. This is the reference an owned copy is
74
+ compared against."""
75
+ source = packaged_source(entry)
76
+ if not entry.runtime:
77
+ return source
78
+ if source.count(_RUNTIME_SWITCH_OFF) != 1:
79
+ raise RuntimeError(
80
+ f"{entry.source_module} must carry exactly one {_RUNTIME_SWITCH_OFF!r} line "
81
+ "for eject to hand its runtime over"
82
+ )
83
+ return source.replace(_RUNTIME_SWITCH_OFF, _RUNTIME_SWITCH_ON)
84
+
85
+
86
+ def packaged_runtime_path(module: str) -> Path:
87
+ """The installed source of helper runtime *module*."""
88
+ package = importlib.import_module("metaobjects.codegen.runtime")
89
+ assert package.__file__ is not None
90
+ return Path(package.__file__).parent / f"{module}.py"
91
+
92
+
93
+ def runtime_path(root: Path, module: str) -> Path:
94
+ return root / RUNTIME_DIR / f"{module}.py"
95
+
96
+
55
97
  def ejectable() -> list[GeneratorEntry]:
56
98
  return [e for e in GENERATOR_REGISTRY.values() if e.source is not None]
57
99
 
@@ -88,9 +130,19 @@ def eject(names: list[str], root: Path, *, force: bool = False) -> EjectResult:
88
130
  for n in names:
89
131
  entry = known[n]
90
132
  target = owned_path(root, n)
91
- target.write_text(packaged_source(entry), encoding="utf-8")
133
+ target.write_text(ejected_source(entry), encoding="utf-8")
92
134
  out.append(f"ejected {n} -> {target.relative_to(root)}")
93
135
  out.append(f" wire it: replace \"{n}\" in `generators` with {owned_token(entry)}")
136
+ for module in entry.runtime:
137
+ rt = runtime_path(root, module)
138
+ rel = rt.relative_to(root)
139
+ if rt.exists():
140
+ out.append(f" runtime {rel}: kept your copy (eject never overwrites runtime)")
141
+ continue
142
+ rt.parent.mkdir(parents=True, exist_ok=True)
143
+ rt.write_text(packaged_runtime_path(module).read_text(encoding="utf-8"),
144
+ encoding="utf-8")
145
+ out.append(f" runtime {rel}: copied — its generated output imports this copy")
94
146
  out.append("")
95
147
  out.append(
96
148
  "The copies are yours: edit them freely. Keep only one of the packaged name and your "
@@ -112,8 +164,12 @@ def owned_status(root: Path, entry: GeneratorEntry) -> str | None:
112
164
  path = owned_path(root, entry.name)
113
165
  if entry.source is None or not path.exists():
114
166
  return None
115
- mine = _normalised(path.read_text(encoding="utf-8"))
116
- ref = _normalised(packaged_source(entry))
167
+ return _compare(path.read_text(encoding="utf-8"), ejected_source(entry))
168
+
169
+
170
+ def _compare(mine_text: str, ref_text: str) -> str:
171
+ mine = _normalised(mine_text)
172
+ ref = _normalised(ref_text)
117
173
  behind = sum((ref - mine).values())
118
174
  own = sum((mine - ref).values())
119
175
  if behind == 0 and own == 0:
@@ -121,6 +177,23 @@ def owned_status(root: Path, entry: GeneratorEntry) -> str | None:
121
177
  return f"DIFFERS: {behind} behind, {own} of your own"
122
178
 
123
179
 
180
+ def runtime_status(root: Path) -> list[tuple[Path, str]]:
181
+ """``(relative path, status)`` for each owned runtime copy, against the installed one.
182
+
183
+ Same verdict shape as :func:`owned_status`, so ``--list`` can say when an upgrade
184
+ changed runtime you own."""
185
+ modules = sorted({m for e in ejectable() for m in e.runtime})
186
+ rows: list[tuple[Path, str]] = []
187
+ for module in modules:
188
+ path = runtime_path(root, module)
189
+ if path.exists():
190
+ rows.append((path.relative_to(root), _compare(
191
+ path.read_text(encoding="utf-8"),
192
+ packaged_runtime_path(module).read_text(encoding="utf-8"),
193
+ )))
194
+ return rows
195
+
196
+
124
197
  def build_owned(spec: str, root: Path, ctx: GeneratorBuildContext) -> tuple[Generator | None, str | None]:
125
198
  """Resolve a ``module:symbol`` generator entry against the project at *root*.
126
199
 
@@ -40,7 +40,10 @@ from metaobjects.codegen.generators.output_prompt_generator import (
40
40
  from metaobjects.codegen.generators.render_helper_generator import (
41
41
  render_helper_generator,
42
42
  )
43
- from metaobjects.codegen.generators.router_generator import router_generator
43
+ from metaobjects.codegen.generators.router_generator import (
44
+ RUNTIME_MODULES as _ROUTER_RUNTIME,
45
+ router_generator,
46
+ )
44
47
  from metaobjects.codegen.generators.template_generator import template_generator
45
48
  from metaobjects.codegen.generators.trace_helper_generator import trace_helper_generator
46
49
  from metaobjects.render.verify import InMemoryProvider
@@ -113,6 +116,13 @@ class GeneratorEntry:
113
116
  #: symbol the copy is wired by. ``None`` = not ejectable (the ``template`` primitive
114
117
  #: has no emit logic of its own to own).
115
118
  source: Callable[..., Generator] | None = None
119
+ #: Helper runtime modules (under ``metaobjects.codegen.runtime``) the generator's
120
+ #: OUTPUT imports. ``metaobjects eject`` copies their source into the adopter's
121
+ #: ``codegen/runtime/`` beside the owned generator, and the owned copy's output imports
122
+ #: that copy instead of the installed package (ADR-0034 Amendment 3: the adopter owns
123
+ #: every helper line its generated code depends on). Core modules — loader, registry,
124
+ #: render, extract, ``ObjectManager`` — are never listed here: they stay package imports.
125
+ runtime: tuple[str, ...] = ()
116
126
 
117
127
  @property
118
128
  def source_module(self) -> str | None:
@@ -186,10 +196,11 @@ GENERATOR_REGISTRY: dict[str, GeneratorEntry] = {
186
196
  # generated package fails at import (ModuleNotFoundError).
187
197
  requires=("entity", "filter-allowlist"),
188
198
  source=router_generator,
199
+ runtime=_ROUTER_RUNTIME,
189
200
  ),
190
201
  "output-parser": GeneratorEntry(
191
202
  name="output-parser",
192
- description="Per-template tolerant output parser (recover-on-receipt).",
203
+ description="Per-template response parser: a strict parse that rejects a reply not matching the @responseRef shape, plus a tolerant, never-raising extract_lenient.",
193
204
  tier="native",
194
205
  layer="capability",
195
206
  factory=lambda _ctx: output_parser_generator(),
@@ -215,7 +226,7 @@ GENERATOR_REGISTRY: dict[str, GeneratorEntry] = {
215
226
  ),
216
227
  "extractor": GeneratorEntry(
217
228
  name="extractor",
218
- description="Per-template strict typed extract<Name> helper (strict payload extraction).",
229
+ description="Per-template typed extract<Name> helper: tolerant recovery of the typed response from dirty model text; raises only when a @required field is lost.",
219
230
  tier="native",
220
231
  layer="capability",
221
232
  factory=lambda _ctx: extractor_generator(),
@@ -9,7 +9,7 @@ arrays-of-objects populated) in ONE call:
9
9
 
10
10
  extract_<snake>(root, text, opts=None) -> <Vo>
11
11
  r = extract_lenient_<snake>_with_loader(root, text, opts) # nested-capable extract
12
- if r.report.has_lost_required(): raise ValueError(...)
12
+ if r.report.has_lost_required() or r.report.has_malformed_required(): raise ValueError(...)
13
13
  return _to_strict_<vo>(r.data) # mirror -> strict mapper
14
14
 
15
15
  Why the loaded ``root``: the SELF-CONTAINED ``extract_<snake>(text)`` leaves nested
@@ -173,16 +173,23 @@ def render_extractor(
173
173
  lines.append(" value-object). Runs the tolerant nested-capable extract, then maps the")
174
174
  lines.append(" extracted mirror graph onto the strict Pydantic payload graph.")
175
175
  lines.append("")
176
- lines.append(" :raises ValueError: iff a ``@required`` field was lost (the strict gate).")
176
+ lines.append(" :raises ValueError: iff a ``@required`` field was lost, or present but")
177
+ lines.append(" unusable (MALFORMED: an undeclared enum member, text for a number).")
177
178
  lines.append(' """')
178
179
  lines.append(f" r = {extract_lenient_with_fn}(root, text, opts)")
179
- lines.append(" if r.report.has_lost_required():")
180
- lines.append(" raise ValueError(")
180
+ lines.append(" if r.report.has_lost_required() or r.report.has_malformed_required():")
181
+ lines.append(" unusable = []")
182
+ lines.append(" if r.report.has_lost_required():")
181
183
  lines.append(
182
- f' "{extract_fn}: lost required field(s): "'
184
+ ' unusable.append("lost required field(s): "'
185
+ ' + ", ".join(r.report.lost_required()))'
183
186
  )
184
- lines.append(' + ", ".join(r.report.lost_required())')
185
- lines.append(" )")
187
+ lines.append(" if r.report.has_malformed_required():")
188
+ lines.append(
189
+ ' unusable.append("malformed required field(s): "'
190
+ ' + ", ".join(r.report.malformed_required()))'
191
+ )
192
+ lines.append(f' raise ValueError("{extract_fn}: " + "; ".join(unusable))')
186
193
  lines.append(f" return {root_mapper}(r.data)")
187
194
  lines.append("")
188
195
  lines.append("")
@@ -39,11 +39,12 @@ pick their preferred persistence layer.
39
39
  from __future__ import annotations
40
40
 
41
41
  from dataclasses import dataclass
42
+ from pathlib import Path
42
43
 
43
44
  from metaobjects.apidocs.naming import route_path as _route_path
44
45
  from metaobjects.apidocs.naming import reverse_finder_fn, reverse_finder_in_fn
45
46
  from metaobjects.apidocs.naming import snake_case as _snake_case
46
- from metaobjects.codegen.constants import generated_header
47
+ from metaobjects.codegen.constants import GENERATED_MARKER, generated_header
47
48
  from metaobjects.codegen.format import ruff_format
48
49
  from metaobjects.codegen.fr010_field_mapping import is_required
49
50
  from metaobjects.codegen.generator import EmittedFile, GenContext, Generator, per_entity
@@ -81,6 +82,61 @@ from metaobjects.naming import DEFAULT_COLUMN_NAMING
81
82
  from metaobjects.shared.base_types import TYPE_IDENTITY
82
83
  from metaobjects.shared.separators import PACKAGE_SEP
83
84
 
85
+ #: Where the emitted router imports its helper runtime (``filter_parser``,
86
+ #: ``constraint_errors``) from. ``False`` — the packaged generator — imports the copy
87
+ #: installed with ``metaobjects``: ``from metaobjects.codegen.runtime.<m> import ...``.
88
+ #: ``metaobjects eject routes`` flips this line to ``True`` in the owned copy, which then
89
+ #: EMITS that runtime's source from ``codegen/runtime/`` (the directory beside the owned
90
+ #: generators, :data:`RUNTIME_SOURCE_DIR`) into the generated package as ``_runtime/`` and
91
+ #: imports it package-relatively. The runtime is then code the adopter owns: edit
92
+ #: ``codegen/runtime/<m>.py``, regenerate, and the generated package carries the change.
93
+ OWNED_RUNTIME = False
94
+
95
+ #: The helper runtime modules the emitted router imports (ADR-0034 Amendment 3: helper
96
+ #: tier, not core). Listed on the registry entry too, which is what ``eject`` copies.
97
+ RUNTIME_MODULES: tuple[str, ...] = ("constraint_errors", "filter_parser")
98
+
99
+ #: The generated subpackage an owned router emits the runtime into.
100
+ RUNTIME_SUBPACKAGE = "_runtime"
101
+
102
+ #: Where an owned router reads the runtime source it emits. The packaged generator and an
103
+ #: ejected copy sit at the same depth relative to their ``runtime/`` sibling
104
+ #: (``metaobjects/codegen/generators`` vs ``codegen/generators``), so one expression
105
+ #: serves both.
106
+ RUNTIME_SOURCE_DIR = Path(__file__).resolve().parent.parent / "runtime"
107
+
108
+
109
+ def _runtime_module(module: str) -> str:
110
+ """The module path an emitted router imports runtime *module* from."""
111
+ if OWNED_RUNTIME:
112
+ return f".{RUNTIME_SUBPACKAGE}.{module}"
113
+ return f"metaobjects.codegen.runtime.{module}"
114
+
115
+
116
+ def _owned_runtime_files() -> list[EmittedFile]:
117
+ """The runtime source an owned router emits beside itself, marked as generated."""
118
+ files = [EmittedFile(
119
+ path=f"{RUNTIME_SUBPACKAGE}/__init__.py",
120
+ content=f"# {GENERATED_MARKER} — DO NOT EDIT.\n"
121
+ f"# Helper runtime for the generated routers. Its source is codegen/runtime/.\n",
122
+ )]
123
+ for module in RUNTIME_MODULES:
124
+ src = RUNTIME_SOURCE_DIR / f"{module}.py"
125
+ if not src.is_file():
126
+ raise FileNotFoundError(
127
+ f"the owned routes generator emits its runtime from {src}, which does not "
128
+ f"exist. Copy {module}.py back from the installed package's "
129
+ "metaobjects/codegen/runtime/ directory (`python -c \"import "
130
+ "metaobjects.codegen.runtime as r; print(r.__path__[0])\"` prints it)."
131
+ )
132
+ files.append(EmittedFile(
133
+ path=f"{RUNTIME_SUBPACKAGE}/{module}.py",
134
+ content=f"# {GENERATED_MARKER} — DO NOT EDIT. Copied from "
135
+ f"codegen/runtime/{module}.py: edit that file and regenerate.\n"
136
+ + src.read_text(encoding="utf-8"),
137
+ ))
138
+ return files
139
+
84
140
 
85
141
  def _effective_fqn(entity: MetaObject) -> str:
86
142
  """``package::name`` via the canonical :meth:`MetaData.resolution_key` (own
@@ -928,10 +984,10 @@ class RouterGenerator:
928
984
  parts.append("from fastapi.responses import JSONResponse")
929
985
  parts.append("from pydantic import BaseModel, ValidationError")
930
986
  parts.append("")
931
- parts.append("from metaobjects.codegen.runtime.constraint_errors import (")
987
+ parts.append(f"from {_runtime_module('constraint_errors')} import (")
932
988
  parts.append(" classify_constraint_error,")
933
989
  parts.append(")")
934
- parts.append("from metaobjects.codegen.runtime.filter_parser import (")
990
+ parts.append(f"from {_runtime_module('filter_parser')} import (")
935
991
  parts.append(" FilterPredicate,")
936
992
  parts.append(" parse_filter,")
937
993
  parts.append(")")
@@ -1280,10 +1336,10 @@ class RouterGenerator:
1280
1336
  parts.append("from fastapi.responses import JSONResponse")
1281
1337
  parts.append("from pydantic import BaseModel, ValidationError")
1282
1338
  parts.append("")
1283
- parts.append("from metaobjects.codegen.runtime.constraint_errors import (")
1339
+ parts.append(f"from {_runtime_module('constraint_errors')} import (")
1284
1340
  parts.append(" classify_constraint_error,")
1285
1341
  parts.append(")")
1286
- parts.append("from metaobjects.codegen.runtime.filter_parser import (")
1342
+ parts.append(f"from {_runtime_module('filter_parser')} import (")
1287
1343
  parts.append(" FilterPredicate,")
1288
1344
  parts.append(" parse_filter,")
1289
1345
  parts.append(")")
@@ -1447,7 +1503,7 @@ class RouterGenerator:
1447
1503
  parts.append("from fastapi.responses import JSONResponse")
1448
1504
  parts.append("from pydantic import BaseModel")
1449
1505
  parts.append("")
1450
- parts.append("from metaobjects.codegen.runtime.filter_parser import (")
1506
+ parts.append(f"from {_runtime_module('filter_parser')} import (")
1451
1507
  parts.append(" FilterPredicate,")
1452
1508
  parts.append(" parse_filter,")
1453
1509
  parts.append(")")
@@ -1528,7 +1584,10 @@ class RouterGenerator:
1528
1584
  )
1529
1585
  ]
1530
1586
 
1531
- return per_entity(emit)(ctx)
1587
+ files = per_entity(emit)(ctx)
1588
+ if OWNED_RUNTIME and files:
1589
+ files.extend(_owned_runtime_files())
1590
+ return files
1532
1591
 
1533
1592
 
1534
1593
  def render_router(
@@ -282,13 +282,18 @@ def render_trace_helper(entity: MetaObject, root: MetaData) -> str | None:
282
282
  " :param redact: optional row redaction applied before the single record.",
283
283
  ' """',
284
284
  " outcome = extract(input.llm_response_text, _RESPONSE_SCHEMA)",
285
- " failed = outcome.report.has_lost_required()",
285
+ " # A required field lost OR malformed (present but unusable) has no value,",
286
+ " # so either fails the call.",
287
+ " unusable = []",
288
+ " if outcome.report.has_lost_required():",
289
+ ' unusable.append("lost required: " + ", ".join(outcome.report.lost_required()))',
290
+ " if outcome.report.has_malformed_required():",
291
+ " unusable.append(",
292
+ ' "malformed required: " + ", ".join(outcome.report.malformed_required())',
293
+ " )",
294
+ " failed = bool(unusable)",
286
295
  " status = STATUS_ERROR if failed else STATUS_OK",
287
- " error_detail = (",
288
- ' "lost required: " + ", ".join(outcome.report.lost_required())',
289
- " if failed",
290
- " else None",
291
- " )",
296
+ ' error_detail = "; ".join(unusable) if failed else None',
292
297
  " # Extraction owns the derived status/error_detail — fold them into the input",
293
298
  " # before building the base row (dataclasses.replace, leaving the original",
294
299
  " # input untouched).",
@@ -0,0 +1,101 @@
1
+ """Everything a generator written from scratch needs, in one import (ADR-0034 Amendment 4).
2
+
3
+ MetaObjects' product is the core; the outputs an application needs beyond the reference
4
+ generators — OpenAPI, JSON Schema, a client, docs — are generators the adopter writes. A
5
+ generator is any object with a ``name`` and a ``generate(ctx) -> list[EmittedFile]``; wire it
6
+ as ``module:symbol`` in ``--generators`` or in ``metaobjects.config.yaml``'s
7
+ ``targets.<name>.generators`` and ``metaobjects gen`` runs it and ``metaobjects verify
8
+ --codegen`` drift-gates it. See ``docs/recipes/write-your-own-generator.md``.
9
+
10
+ These helpers answer the questions whose right answer is a rule the engine already owns.
11
+ Before this module they were spread over five internal modules, one of them private, and the
12
+ obvious spelling of several was wrong:
13
+
14
+ * **Python's ``attr(name)`` is OWN-ONLY** (ADR-0039's naming inversion). A field that inherits
15
+ ``@maxLength`` or ``@required`` through ``extends`` reads ``None`` through it. Read effective
16
+ values with ``node.attrs().get(name)`` — or the helpers below, which do.
17
+ * ``field.is_array`` is the raw own flag; :func:`field_is_array` resolves.
18
+ * An ``@objectRef`` is resolved package-locally (ADR-0042), never by short name.
19
+ """
20
+ from __future__ import annotations
21
+
22
+ from typing import Any
23
+
24
+ from metaobjects.apidocs.naming import route_path
25
+ from metaobjects.codegen.generator import (
26
+ EmittedFile,
27
+ GenContext,
28
+ Generator,
29
+ once_per_run,
30
+ per_entity,
31
+ per_model,
32
+ per_package,
33
+ )
34
+ from metaobjects.codegen.instance_artifacts import is_abstract
35
+ from metaobjects.codegen.template_codegen.template_data import package_of
36
+ from metaobjects.codegen.type_map import effective_enum_values, field_is_array
37
+ from metaobjects.codegen.value_objects import object_ref_target
38
+ from metaobjects.meta.core.field.field_constants import FIELD_ATTR_MAX_LENGTH, FIELD_ATTR_REQUIRED
39
+ from metaobjects.naming import to_snake_case
40
+
41
+ __all__ = [
42
+ "EmittedFile",
43
+ "GenContext",
44
+ "Generator",
45
+ "description",
46
+ "enum_values",
47
+ "field_is_array",
48
+ "is_abstract",
49
+ "is_required",
50
+ "max_length",
51
+ "object_ref_target",
52
+ "once_per_run",
53
+ "package_of",
54
+ "per_entity",
55
+ "per_model",
56
+ "per_package",
57
+ "primary_key_fields",
58
+ "route_path",
59
+ "to_snake_case",
60
+ ]
61
+
62
+ _TYPE_VALIDATOR = "validator"
63
+ _VALIDATOR_REQUIRED = "required"
64
+ _DOC_DESCRIPTION = "description"
65
+ _IDENTITY_FIELDS = "fields"
66
+
67
+
68
+ def is_required(field: Any) -> bool:
69
+ """Effective required-ness: ``@required: true`` or a ``validator.required`` child, own or
70
+ inherited through ``extends`` — the same rule the neutral template data uses."""
71
+ if field.attrs().get(FIELD_ATTR_REQUIRED) is True:
72
+ return True
73
+ return any(c.type == _TYPE_VALIDATOR and c.sub_type == _VALIDATOR_REQUIRED for c in field.children())
74
+
75
+
76
+ def max_length(field: Any) -> int | None:
77
+ """Effective ``@maxLength``, or ``None``."""
78
+ v = field.attrs().get(FIELD_ATTR_MAX_LENGTH)
79
+ return int(v) if isinstance(v, (int, float)) and not isinstance(v, bool) else None
80
+
81
+
82
+ def enum_values(field: Any) -> list[str]:
83
+ """A ``field.enum``'s effective ``@values`` (empty when absent)."""
84
+ return effective_enum_values(field)
85
+
86
+
87
+ def description(node: Any) -> str | None:
88
+ """The node's effective ``@description``, or ``None``."""
89
+ v = node.attrs().get(_DOC_DESCRIPTION)
90
+ return v if isinstance(v, str) else None
91
+
92
+
93
+ def primary_key_fields(obj: Any) -> list[str]:
94
+ """The primary identity's field names, own or inherited (empty when none)."""
95
+ identity = obj.primary_identity()
96
+ if identity is None:
97
+ return []
98
+ v = identity.attrs().get(_IDENTITY_FIELDS)
99
+ if isinstance(v, str):
100
+ return [s.strip() for s in v.split(",") if s.strip()]
101
+ return [str(x) for x in v] if isinstance(v, (list, tuple)) else []
@@ -130,9 +130,13 @@ def run_gen(
130
130
  # to out_dir). The generated modules use package-relative imports, so without
131
131
  # this a consumer can't import them. A generator-emitted __init__.py wins; a
132
132
  # hand-authored one is left untouched by the overwrite policy below.
133
+ #
134
+ # Only directories that hold a generated PYTHON module: an adopter's own generator
135
+ # emitting JSON Schema or OpenAPI into `schemas/` is not writing a package, and an
136
+ # `__init__.py` there is litter that makes the tree look importable.
133
137
  if config.emit_package_init:
134
138
  pkg_rel_dirs: set[str] = set()
135
- for full in list(emitted):
139
+ for full in [p for p in emitted if p.endswith(".py")]:
136
140
  d = os.path.dirname(os.path.relpath(full, config.out_dir))
137
141
  while True:
138
142
  pkg_rel_dirs.add(d)