metaobjects 0.25.0__tar.gz → 1.0.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 (441) hide show
  1. {metaobjects-0.25.0 → metaobjects-1.0.0}/PKG-INFO +1 -1
  2. {metaobjects-0.25.0 → metaobjects-1.0.0}/pyproject.toml +1 -1
  3. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/agent_context/scaffold.py +105 -21
  4. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/cli.py +159 -15
  5. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/config.py +33 -0
  6. metaobjects-1.0.0/src/metaobjects/codegen/format.py +72 -0
  7. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/names_generator.py +20 -16
  8. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/router_generator.py +46 -4
  9. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/overwrite_policy.py +69 -21
  10. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/runner.py +55 -3
  11. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/registry_manifest.py +1 -1
  12. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/source_resolution.py +26 -6
  13. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/ui.json +2 -2
  14. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_cli.py +103 -0
  15. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_cli_config_verify.py +54 -0
  16. metaobjects-1.0.0/tests/codegen/test_format.py +85 -0
  17. metaobjects-1.0.0/tests/codegen/test_gen_state_keying.py +182 -0
  18. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_names_extends_chain.py +42 -10
  19. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_runner.py +96 -0
  20. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/api_contract_server.py +15 -1
  21. metaobjects-1.0.0/tests/unit/test_agent_context_staleness.py +200 -0
  22. {metaobjects-0.25.0 → metaobjects-1.0.0}/uv.lock +1 -1
  23. metaobjects-0.25.0/src/metaobjects/codegen/format.py +0 -42
  24. metaobjects-0.25.0/tests/codegen/test_format.py +0 -30
  25. metaobjects-0.25.0/tests/unit/test_agent_context_staleness.py +0 -102
  26. {metaobjects-0.25.0 → metaobjects-1.0.0}/.gitignore +0 -0
  27. {metaobjects-0.25.0 → metaobjects-1.0.0}/LICENSE +0 -0
  28. {metaobjects-0.25.0 → metaobjects-1.0.0}/README.md +0 -0
  29. {metaobjects-0.25.0 → metaobjects-1.0.0}/hatch_build.py +0 -0
  30. {metaobjects-0.25.0 → metaobjects-1.0.0}/scripts/generate_embedded_library.py +0 -0
  31. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/__init__.py +0 -0
  32. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/agent_context/__init__.py +0 -0
  33. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/apidocs/__init__.py +0 -0
  34. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/apidocs/api_model.py +0 -0
  35. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/apidocs/builder.py +0 -0
  36. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/apidocs/naming.py +0 -0
  37. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/apidocs/paths.py +0 -0
  38. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/apidocs/renderer.py +0 -0
  39. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/attr_class_map.py +0 -0
  40. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  41. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/__init__.py +0 -0
  42. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/collision_names.py +0 -0
  43. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/constants.py +0 -0
  44. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  45. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  46. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  47. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generator.py +0 -0
  48. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generator_registry.py +0 -0
  49. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
  50. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  51. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  52. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  53. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/find_inbound.py +0 -0
  54. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  55. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  56. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  57. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  58. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
  59. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  60. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  61. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  62. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  63. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  64. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/metaobjects-config.schema.json +0 -0
  65. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  66. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/project_config.py +0 -0
  67. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  68. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  69. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  70. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  71. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  72. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  73. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/codegen/type_map.py +0 -0
  74. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/config/__init__.py +0 -0
  75. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/config/neutral_config.py +0 -0
  76. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/config/source_resolver.py +0 -0
  77. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/core_types.py +0 -0
  78. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/datatype.py +0 -0
  79. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/documentation/__init__.py +0 -0
  80. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/documentation/doc_constants.py +0 -0
  81. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/documentation/doc_provider.py +0 -0
  82. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/documentation/doc_schema.py +0 -0
  83. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/errors.py +0 -0
  84. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/library/__init__.py +0 -0
  85. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/library/embedded_library.py +0 -0
  86. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/library/library_sources.py +0 -0
  87. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/__init__.py +0 -0
  88. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/merge.py +0 -0
  89. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
  90. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/registered_validation.py +0 -0
  91. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/sources/__init__.py +0 -0
  92. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
  93. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/sources/file_source.py +0 -0
  94. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  95. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
  96. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/validate_discriminator.py +0 -0
  97. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
  98. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/validate_field_mutability.py +0 -0
  99. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  100. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  101. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  102. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/loader/validation_passes.py +0 -0
  103. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/__init__.py +0 -0
  104. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/__init__.py +0 -0
  105. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  106. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  107. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  108. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/field/__init__.py +0 -0
  109. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  110. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  111. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  112. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  113. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  114. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/index/__init__.py +0 -0
  115. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  116. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  117. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/object/__init__.py +0 -0
  118. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  119. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  120. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  121. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  122. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  123. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
  124. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  125. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  126. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  127. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  128. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
  129. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/requirement/meta_requirement.py +0 -0
  130. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/requirement/requirement_constants.py +0 -0
  131. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  132. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  133. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/meta_data.py +0 -0
  134. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/meta_root.py +0 -0
  135. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/__init__.py +0 -0
  136. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  137. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  138. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  139. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  140. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  141. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  142. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  143. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  144. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  145. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/__init__.py +0 -0
  146. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  147. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  148. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  149. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  150. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  151. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  152. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  153. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  154. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/provider_extends.py +0 -0
  155. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/template/__init__.py +0 -0
  156. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/template/meta_template.py +0 -0
  157. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  158. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/meta/template/template_constants.py +0 -0
  159. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/naming.py +0 -0
  160. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/naming_refs.py +0 -0
  161. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/parser.py +0 -0
  162. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/parser_yaml.py +0 -0
  163. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/provider.py +0 -0
  164. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/py.typed +0 -0
  165. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/registry.py +0 -0
  166. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/__init__.py +0 -0
  167. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/email_document.py +0 -0
  168. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/escapers.py +0 -0
  169. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  170. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/__init__.py +0 -0
  171. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/coerce.py +0 -0
  172. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/extract.py +0 -0
  173. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/extract_map.py +0 -0
  174. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  175. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/locate.py +0 -0
  176. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/normalize.py +0 -0
  177. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/strip.py +0 -0
  178. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/types.py +0 -0
  179. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  180. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/filesystem_provider.py +0 -0
  181. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/payload_accessors.py +0 -0
  182. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/prompt/__init__.py +0 -0
  183. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  184. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  185. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  186. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  187. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  188. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/renderer.py +0 -0
  189. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/render/verify.py +0 -0
  190. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/runtime/__init__.py +0 -0
  191. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
  192. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  193. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/runtime/object_manager.py +0 -0
  194. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/runtime/tph.py +0 -0
  195. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/serializer_json.py +0 -0
  196. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/shared/__init__.py +0 -0
  197. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/shared/base_types.py +0 -0
  198. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/shared/separators.py +0 -0
  199. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/shared/structural.py +0 -0
  200. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/source/__init__.py +0 -0
  201. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/source/error_source.py +0 -0
  202. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/source/json_path.py +0 -0
  203. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/source/semantic_diff.py +0 -0
  204. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/source/yaml_positions.py +0 -0
  205. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  206. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/attr.json +0 -0
  207. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/db.json +0 -0
  208. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/documentation.json +0 -0
  209. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/field.json +0 -0
  210. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
  211. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/index.json +0 -0
  212. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
  213. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/object.json +0 -0
  214. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/origin.json +0 -0
  215. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  216. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  217. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/requirement.json +0 -0
  218. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/source.json +0 -0
  219. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/template.json +0 -0
  220. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  221. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
  222. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/spec_metamodel/view.json +0 -0
  223. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/super_resolve.py +0 -0
  224. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/validation_types.py +0 -0
  225. {metaobjects-0.25.0 → metaobjects-1.0.0}/src/metaobjects/yaml_desugar.py +0 -0
  226. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/__init__.py +0 -0
  227. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/__init__.py +0 -0
  228. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  229. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/extends/expected/Program.py +0 -0
  230. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/extends/meta.json +0 -0
  231. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  232. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  233. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/nested-array/meta.json +0 -0
  234. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  235. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  236. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/scalars/meta.json +0 -0
  237. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  238. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/golden/vanilla/meta.json +0 -0
  239. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_abstract_conformance.py +0 -0
  240. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_api_docs_builder.py +0 -0
  241. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_api_docs_paths.py +0 -0
  242. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_cli_config_gen.py +0 -0
  243. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_cli_providers.py +0 -0
  244. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_cli_registry.py +0 -0
  245. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  246. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_cli_verify_strict.py +0 -0
  247. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  248. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_constants_config.py +0 -0
  249. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_entity_model.py +0 -0
  250. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_enum_conformance.py +0 -0
  251. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  252. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_extract_tier_collision.py +0 -0
  253. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_extractor_generator.py +0 -0
  254. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  255. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  256. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
  257. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  258. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_generator.py +0 -0
  259. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_generator_extension_seams.py +0 -0
  260. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_golden.py +0 -0
  261. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  262. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_inheritance_conformance.py +0 -0
  263. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_instance_artifacts.py +0 -0
  264. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_m2m_codegen.py +0 -0
  265. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_names_generator.py +0 -0
  266. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_no_magic_physical_names.py +0 -0
  267. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_origin_nullability.py +0 -0
  268. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_output_parser_generator.py +0 -0
  269. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_overwrite_policy.py +0 -0
  270. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_overwrite_policy_hash.py +0 -0
  271. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_payload_vo_generator.py +0 -0
  272. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_project_config.py +0 -0
  273. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_projection_compile.py +0 -0
  274. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_render_helper_conformance.py +0 -0
  275. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_render_helper_generator.py +0 -0
  276. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_reverse_finders.py +0 -0
  277. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_router_autoset.py +0 -0
  278. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_router_generator.py +0 -0
  279. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_source_resolution.py +0 -0
  280. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_sourceless_projection.py +0 -0
  281. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_template_data.py +0 -0
  282. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_template_generator.py +0 -0
  283. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_template_output_pattern.py +0 -0
  284. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_template_scope_helpers.py +0 -0
  285. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_template_scope_walk.py +0 -0
  286. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_template_spec.py +0 -0
  287. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_tph_codegen.py +0 -0
  288. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_trace_helper_generator.py +0 -0
  289. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_type_map.py +0 -0
  290. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_validation_conformance.py +0 -0
  291. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/codegen/test_write_through.py +0 -0
  292. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/config/__init__.py +0 -0
  293. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/config/test_neutral_config.py +0 -0
  294. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/config/test_source_resolver.py +0 -0
  295. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/__init__.py +0 -0
  296. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/capabilities.py +0 -0
  297. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/conformance-expected-failures.json +0 -0
  298. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/conformance_adapter.py +0 -0
  299. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/corpus.py +0 -0
  300. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/expected_failures.py +0 -0
  301. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/fixture_discovery.py +0 -0
  302. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/navigator.py +0 -0
  303. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  304. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_conformance.py +0 -0
  305. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_extract_conformance.py +0 -0
  306. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_extract_object_verdict.py +0 -0
  307. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  308. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_fr011_attrs.py +0 -0
  309. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
  310. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_object_model_conformance.py +0 -0
  311. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_provider_composition_conformance.py +0 -0
  312. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_registry_conformance.py +0 -0
  313. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_runner_hardfail.py +0 -0
  314. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_source_resolution_conformance.py +0 -0
  315. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  316. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_strict_attr_load.py +0 -0
  317. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_template_codegen_conformance.py +0 -0
  318. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_template_generator_conformance.py +0 -0
  319. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/test_yaml_conformance.py +0 -0
  320. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  321. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/__init__.py +0 -0
  322. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/api_contract_assertions.py +0 -0
  323. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/api_contract_jsonb_server.py +0 -0
  324. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/api_contract_m2m_server.py +0 -0
  325. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/generated_jsonb_app.py +0 -0
  326. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/generated_m2m_app.py +0 -0
  327. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/generated_router_app.py +0 -0
  328. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/generated_tph_app.py +0 -0
  329. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/meta_ai_trace.yaml +0 -0
  330. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/normalization.py +0 -0
  331. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/postgres_container.py +0 -0
  332. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/query_runner.py +0 -0
  333. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/scenarios.py +0 -0
  334. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_api_contract.py +0 -0
  335. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_api_contract_generated.py +0 -0
  336. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_api_contract_jsonb.py +0 -0
  337. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_api_contract_m2m.py +0 -0
  338. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  339. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
  340. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  341. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_llm_call_trace.py +0 -0
  342. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_normalization.py +0 -0
  343. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_query_scenarios.py +0 -0
  344. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/integration/test_runtime_return_types.py +0 -0
  345. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/loader/__init__.py +0 -0
  346. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/loader/test_abstract_subtype_authored.py +0 -0
  347. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/loader/test_enum_extends_values_conflict.py +0 -0
  348. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/loader/test_field_mutability_order.py +0 -0
  349. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/loader/test_shipped_library_ai.py +0 -0
  350. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/loader/test_template_payload_target_210.py +0 -0
  351. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/open_closed_proof_test.py +0 -0
  352. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/__init__.py +0 -0
  353. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/__init__.py +0 -0
  354. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_coerce.py +0 -0
  355. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_extract.py +0 -0
  356. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_extract_map.py +0 -0
  357. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  358. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_locate.py +0 -0
  359. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_model.py +0 -0
  360. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_normalize.py +0 -0
  361. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_strip.py +0 -0
  362. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  363. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/prompt/__init__.py +0 -0
  364. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
  365. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/test_email_document.py +0 -0
  366. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/test_filesystem_provider.py +0 -0
  367. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/test_output_format_renderer_nested.py +0 -0
  368. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/test_output_prompt_conformance.py +0 -0
  369. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/test_render_conformance.py +0 -0
  370. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/test_render_max_chars.py +0 -0
  371. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/test_verify.py +0 -0
  372. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/render/test_verify_conformance.py +0 -0
  373. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/runtime/test_object_manager_enum_intvaluemap.py +0 -0
  374. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/runtime/test_object_manager_table_name.py +0 -0
  375. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/source/__init__.py +0 -0
  376. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
  377. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
  378. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
  379. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/source/test_json_path.py +0 -0
  380. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/source/test_semantic_diff.py +0 -0
  381. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/source/test_source_on_node.py +0 -0
  382. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/source/test_yaml_positions.py +0 -0
  383. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/test_api_docs_accuracy.py +0 -0
  384. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/__init__.py +0 -0
  385. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_capabilities.py +0 -0
  386. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_column_naming_strategy.py +0 -0
  387. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_common_attrs.py +0 -0
  388. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_core_types.py +0 -0
  389. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_effective_children_sibling_shadowing.py +0 -0
  390. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_effective_package.py +0 -0
  391. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_errors.py +0 -0
  392. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_field_enum.py +0 -0
  393. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_field_enum_intvaluemap.py +0 -0
  394. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_field_map_validation.py +0 -0
  395. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  396. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  397. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_index_lookup.py +0 -0
  398. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_llm_recorder.py +0 -0
  399. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_loader.py +0 -0
  400. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_loader_bom.py +0 -0
  401. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_loader_class.py +0 -0
  402. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_merge.py +0 -0
  403. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_meta_attr.py +0 -0
  404. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_meta_data.py +0 -0
  405. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_meta_source.py +0 -0
  406. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_module_shortcuts.py +0 -0
  407. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_n2m_resolver.py +0 -0
  408. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  409. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_no_dead_config_fields.py +0 -0
  410. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  411. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  412. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_object_manager_write_through.py +0 -0
  413. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_one_primary_source.py +0 -0
  414. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_parser.py +0 -0
  415. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_parser_yaml.py +0 -0
  416. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_provider.py +0 -0
  417. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_provider_extension.py +0 -0
  418. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_registry.py +0 -0
  419. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_registry_completeness.py +0 -0
  420. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_registry_extend.py +0 -0
  421. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_registry_sealed.py +0 -0
  422. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_relationship_referential_actions.py +0 -0
  423. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_resolution_key.py +0 -0
  424. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  425. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_serializer.py +0 -0
  426. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_shared_constants.py +0 -0
  427. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_smoke.py +0 -0
  428. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_source_escapes.py +0 -0
  429. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_sources.py +0 -0
  430. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_strict_child_placement.py +0 -0
  431. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_super_resolve.py +0 -0
  432. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_template_toolcall.py +0 -0
  433. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  434. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_validation_attr_schema.py +0 -0
  435. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_validation_filter_values.py +0 -0
  436. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_validation_origin_195.py +0 -0
  437. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_validation_origin_paths.py +0 -0
  438. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_validation_sort_field.py +0 -0
  439. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_validation_warnings.py +0 -0
  440. {metaobjects-0.25.0 → metaobjects-1.0.0}/tests/unit/test_write_coercion_arrays.py +0 -0
  441. {metaobjects-0.25.0 → metaobjects-1.0.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.25.0
3
+ Version: 1.0.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.25.0"
3
+ version = "1.0.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"
@@ -80,31 +80,112 @@ class Manifest:
80
80
  # The sentinel a port stamps when it cannot resolve its own installed version.
81
81
  _UNRESOLVED_VERSION = "0.0.0"
82
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".
83
+ #: A release with an optional PRE-RELEASE segment, in either spelling one ecosystem uses.
84
+ #: npm/NuGet/Maven write ``1.0.0-rc.5``; PEP 440 writes ``1.0.0rc5``. Both are the SAME
85
+ #: release, and comparing their SPELLINGS is what made the nudge unsatisfiable — see
86
+ #: ``_same_release``.
87
+ _PRERELEASE_RE = re.compile(
88
+ r"^(\d+)\.(\d+)\.(\d+)" # release
89
+ r"(?:-?([A-Za-z]+)\.?(\d+)?)?$" # optional pre-release: -rc.5 | rc5 | -beta | b2
90
+ )
91
+
92
+
93
+ #: PEP 440 abbreviates the pre-release labels that npm spells out, and normalizing to ONE
94
+ #: of them is what makes `1.1.0-beta.2` and `1.1.0b2` the same release. Only the spellings
95
+ #: PEP 440 itself defines as equivalent — this is a rename table, not a similarity guess.
96
+ _PEP440_LABEL_ALIASES = {
97
+ "a": "alpha",
98
+ "b": "beta",
99
+ "c": "rc",
100
+ "pre": "rc",
101
+ "preview": "rc",
102
+ }
103
+
104
+
105
+ def _release_coordinate(version: object) -> tuple[int, int, int, str, int] | None:
106
+ """``(major, minor, patch, pre_label, pre_number)`` for a version in ANY ecosystem
107
+ spelling.
108
+
109
+ The MAJOR is INFORMATION on this port, and both helpers here used to drop it. That
110
+ reduction is correct on the JVM and only there: Maven Central carries a historical
111
+ major of npm-major + 7 by design, so ``7.24.1`` and ``0.24.1`` genuinely name one
112
+ release and the coordinate must ignore the major to see it. Nothing of the kind holds
113
+ here — ``generatedBy`` is stamped by the Node CLI (an npm version) and
114
+ :func:`installed_metaobjects_version` reads the PyPI distribution version, and npm and
115
+ PyPI share the major by policy. Dropping it made ``1.0.0`` and ``2.0.0`` compare EQUAL,
116
+ and made a ``0.25.0``-stamped context read as NEWER than a ``1.0.0`` install — so the
117
+ 1.0 upgrade, the single upgrade this advisory exists to catch, produced no nudge.
118
+
119
+ The pre-release label is lower-cased and its separators dropped, so ``-rc.5``,
120
+ ``rc5`` and ``-RC5`` all reduce to ``("rc", 5)``. A final release sorts ABOVE every
121
+ pre-release of the same number, which is what ``("", 0)`` vs ``("rc", 5)`` gives under
122
+ tuple ordering only because ``""`` < ``"rc"`` — so finals carry a sentinel label that
123
+ sorts last instead.
124
+
125
+ ``None`` means "not orderable, so nudge": the ``0.0.0`` unresolved-install sentinel,
126
+ build metadata, or anything this cannot parse. Those must never assert "in sync".
98
127
  """
99
128
  if not isinstance(version, str):
100
129
  return None
101
130
  v = version.strip()
102
131
  if v == _UNRESOLVED_VERSION:
103
132
  return None
104
- m = _RELEASE_RE.match(v)
133
+ m = _PRERELEASE_RE.match(v)
105
134
  if m is None:
106
135
  return None
107
- return int(m.group(2)), int(m.group(3))
136
+ label = _PEP440_LABEL_ALIASES.get((m.group(4) or "").lower(), (m.group(4) or "").lower())
137
+ number = int(m.group(5)) if m.group(5) is not None else 0
138
+ # A FINAL release is newer than every pre-release of the same number. "~" sorts above
139
+ # every ASCII letter, so the sentinel does that with plain tuple comparison.
140
+ return (
141
+ int(m.group(1)),
142
+ int(m.group(2)),
143
+ int(m.group(3)),
144
+ label if label else "~",
145
+ number,
146
+ )
147
+
148
+
149
+ def _same_release(generated_by: object, current_version: str) -> bool:
150
+ """True when two version strings name the SAME release in different spellings.
151
+
152
+ The nudge compared spellings, so on the 1.0 RC line it could never be satisfied: the
153
+ canonical scaffolder is the Node CLI (ADR-0033), which stamps npm's ``1.0.0-rc.5``,
154
+ while this port reports PEP 440's ``1.0.0rc5``. Those are one release written two
155
+ ways, ``==`` says otherwise, and the remedy re-runs the scaffolder, which re-stamps
156
+ the same npm string — so the advisory fires on every invocation, forever, including
157
+ when the context is perfectly in sync. That is issue #347 with a different pair of
158
+ ecosystems; the fix for it reasoned about ORDERING and never about spelling.
159
+
160
+ Deliberately narrow. Only two versions that parse as the same release coordinate are
161
+ equal; anything unparseable (including the ``0.0.0`` sentinel) still nudges, and an
162
+ ``rc.4`` context against an ``rc.5`` install is a REAL difference and still nudges.
163
+ """
164
+ stamped = _release_coordinate(generated_by)
165
+ installed = _release_coordinate(current_version)
166
+ return stamped is not None and stamped == installed
167
+
168
+
169
+ def _release_series(version: object) -> tuple[int, int, int] | None:
170
+ """Ordered release coordinate ``(major, minor, patch)``, or ``None`` when not orderable.
171
+
172
+ Derived from :func:`_release_coordinate` rather than parsed again. These two functions
173
+ used to answer the same grammar with two different regexes — differential-checked
174
+ identical over 200k inputs, which is the good outcome of a coin flip, not a guarantee.
175
+ The ordering coordinate simply IS the equality coordinate restricted to FINAL releases,
176
+ and saying so in code means the two cannot drift apart.
177
+
178
+ ``None`` means "not orderable, so nudge", and deliberately covers prereleases
179
+ (``0.24.5-rc.1``, ``1.0.0rc5``), build metadata (``0.24.5+abc``) and the ``0.0.0``
180
+ sentinel. Each must keep nudging: an RC-scaffolded context against a final release is
181
+ still worth refreshing, and an unknown install must never assert "in sync".
182
+ """
183
+ coord = _release_coordinate(version)
184
+ # A pre-release carries a real label; a final carries the "~" sentinel. Only finals are
185
+ # orderable here, which is what the previous release-only regex enforced by construction.
186
+ if coord is None or coord[3] != "~":
187
+ return None
188
+ return coord[0], coord[1], coord[2]
108
189
 
109
190
 
110
191
  def _context_is_ahead_of_install(generated_by: object, current_version: str) -> bool:
@@ -141,16 +222,19 @@ def agent_context_staleness(
141
222
  is in sync — and a one-line advisory message otherwise. Pure + advisory:
142
223
  never raises, never blocks, never writes.
143
224
 
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.
225
+ ANY REAL drift nudges (a re-scaffold is cheap + idempotent), so this is not a semver
226
+ compare an ``rc.4`` context against an ``rc.5`` install is still a reason to
227
+ refresh. Two exemptions, both for cases where nudging can never be SATISFIED:
228
+ ``_same_release`` (one release, two ecosystem spellings) and
229
+ ``_context_is_ahead_of_install`` (a port legitimately behind the npm scaffolder).
148
230
  """
149
231
  if manifest is None:
150
232
  return None # no agent context here → nothing to nudge
151
233
  generated_by = manifest.get("generatedBy")
152
234
  if generated_by == current_version:
153
235
  return None # in sync
236
+ if _same_release(generated_by, current_version):
237
+ return None # one release, two ecosystem spellings — see below
154
238
  if _context_is_ahead_of_install(generated_by, current_version):
155
239
  return None # scaffolded by a NEWER release than this install — see below
156
240
  frm = generated_by if generated_by else "an older MetaObjects"
@@ -44,6 +44,7 @@ from __future__ import annotations
44
44
  import argparse
45
45
  import dataclasses
46
46
  import json
47
+ import os
47
48
  import re
48
49
  import sys
49
50
  import tempfile
@@ -376,6 +377,9 @@ def _run_suite(
376
377
  emit_package_init: bool = True,
377
378
  gen_state_dir: str | None = None,
378
379
  column_naming: str = DEFAULT_COLUMN_NAMING,
380
+ project_root: str | None = None,
381
+ baseline: str = "default",
382
+ refused_out: list[str] | None = None,
379
383
  ) -> list[str]:
380
384
  """Run a generator suite against an ALREADY-LOADED ``root`` into ``out_dir``.
381
385
 
@@ -405,11 +409,26 @@ def _run_suite(
405
409
  emit_package_init=emit_package_init,
406
410
  gen_state_dir=gen_state_dir,
407
411
  column_naming=column_naming,
412
+ # Manifest keys are relative to the PROJECT, which is what the manifest is
413
+ # anchored on — not to ``--out``, which varies per run while the manifest does
414
+ # not. Passed alongside gen_state_dir because the two are one decision: state
415
+ # that lives in the project must be keyed by the project.
416
+ project_root=project_root,
417
+ # `--baseline=adopt`: record what is on disk and write nothing. The refusal it
418
+ # exists for is the pre-manifest one, so it reaches every real gen path and no
419
+ # verify path (those pass gen_state_dir=None and record nothing at all).
420
+ baseline=baseline,
408
421
  )
409
422
  suite = generators if generators is not None else _default_generators()
410
423
  result = run_gen(config, root, generators=suite, entity_filter=entity_filter)
411
424
  for warning in result.warnings:
412
425
  print(f"warning: {warning}")
426
+ # A refusal is a FAILED gate, and the return value carries only what was WRITTEN — so
427
+ # without this the one fact a CI step reads (the exit code) could not see it. An
428
+ # out-list rather than a changed return type: five call sites read the written paths,
429
+ # and only the ones that own an exit code care about the refusals.
430
+ if refused_out is not None:
431
+ refused_out.extend(path for path, status in result.files if status == "refused")
413
432
  return [path for path, status in result.files if status != "refused"]
414
433
 
415
434
 
@@ -463,12 +482,17 @@ def _generate(
463
482
  def gen_state_dir_for(metadata_dir: str) -> str:
464
483
  """Where a project's codegen hash manifest lives.
465
484
 
466
- Anchored on the project — the metadata dir's parent, i.e. the directory holding
467
- ``metaobjects/`` never on ``Path.cwd()``, which is whatever directory the process
468
- happens to sit in and would scatter ``.metaobjects/`` into any package that merely
469
- runs a generator.
485
+ Anchored on the project — never on ``Path.cwd()``, which is whatever directory the
486
+ process happens to sit in and would scatter ``.metaobjects/`` into any package that
487
+ merely runs a generator.
488
+
489
+ It resolves the project through :func:`project_root_for` rather than repeating
490
+ ``Path(metadata_dir).resolve().parent``. That function's own docstring already claimed
491
+ this one "is anchored here" while the code spelled the rule out a second time, and the
492
+ manifest key and the manifest LOCATION have to agree on where the project is or a
493
+ lookup finds nothing and every file reads as "not ours".
470
494
  """
471
- return str(Path(metadata_dir).resolve().parent / ".metaobjects" / ".gen-state")
495
+ return str(project_root_for(metadata_dir) / ".metaobjects" / ".gen-state")
472
496
 
473
497
 
474
498
  #: The conventional declarative-template-spec file (SP-1 §4), discovered when
@@ -792,10 +816,16 @@ def _cmd_gen(args: argparse.Namespace) -> int:
792
816
  print(f" {msg}", file=sys.stderr)
793
817
  return 1
794
818
  gen_state = gen_state_dir_for(args.metadata_dir)
819
+ # Same anchor the manifest itself uses. Both passes below key against it, so a
820
+ # `--template-spec` artifact and a default-suite artifact land in one key space.
821
+ gen_project_root = str(project_root_for(args.metadata_dir))
795
822
  column_naming = getattr(args, "column_naming", None) or DEFAULT_COLUMN_NAMING
823
+ baseline = getattr(args, "baseline", None) or "default"
824
+ refused: list[str] = []
796
825
  written = _run_suite(
797
826
  root, args.out, generators, entities,
798
827
  gen_state_dir=gen_state, column_naming=column_naming,
828
+ project_root=gen_project_root, baseline=baseline, refused_out=refused,
799
829
  )
800
830
  if spec_gens:
801
831
  # The template-spec pass renders user-supplied templates: a bad ref or a
@@ -807,6 +837,7 @@ def _cmd_gen(args: argparse.Namespace) -> int:
807
837
  spec_written = _run_suite(
808
838
  root, args.out, spec_gens, entities, emit_package_init=False,
809
839
  gen_state_dir=gen_state, column_naming=column_naming,
840
+ project_root=gen_project_root, baseline=baseline, refused_out=refused,
810
841
  )
811
842
  except RenderError as exc:
812
843
  print(
@@ -819,6 +850,17 @@ def _cmd_gen(args: argparse.Namespace) -> int:
819
850
  for path in written:
820
851
  print(path)
821
852
  print(f"metaobjects gen: wrote {len(written)} file(s) to {args.out}")
853
+ if refused:
854
+ # A refusal is a failed gate, matching TypeScript's `meta gen` (exit 1). Python and
855
+ # C# used to warn and exit 0, so an adopter wiring this into CI as a drift gate was
856
+ # green while codegen refused to write — a gate that did not gate. The warnings
857
+ # above carry the detail; this line carries the verdict.
858
+ print(
859
+ f"metaobjects gen: FAILED — {len(refused)} file(s) refused (see the warnings "
860
+ f"above; `--baseline=adopt` records what you have and writes nothing)",
861
+ file=sys.stderr,
862
+ )
863
+ return 1
822
864
  return 0
823
865
 
824
866
 
@@ -828,9 +870,17 @@ def _run_gen_targets(
828
870
  root: MetaData,
829
871
  *,
830
872
  gen_state_dir: str | None = None,
873
+ project_root: str | None = None,
874
+ baseline: str = "default",
875
+ refused_out: list[str] | None = None,
831
876
  ) -> tuple[list[str], list[str]]:
832
877
  """Run each target's suite into its ``outDir``. Returns (all_written, errors).
833
878
 
879
+ This is the path the manifest keying matters most on: every target writes into its
880
+ OWN ``outDir`` while all of them share ONE ``gen_state_dir``, so out-dir-relative
881
+ keys meant target B's hash could claim ownership of target A's identically-named
882
+ file. ``project_root`` is the common anchor that makes the keys distinct.
883
+
834
884
  Cross-target duplicate-output-path guard (#267): ``run_gen``'s collision guard
835
885
  is per-pass, so two targets writing the same full path would silently clobber
836
886
  (generated files carry the @generated header). We accumulate every written full
@@ -851,7 +901,8 @@ def _run_gen_targets(
851
901
  out_dir = config.out_dir_for(t)
852
902
  try:
853
903
  written = _run_suite(
854
- root, out_dir, gens, t.entities, gen_state_dir=gen_state_dir
904
+ root, out_dir, gens, t.entities, gen_state_dir=gen_state_dir,
905
+ project_root=project_root, baseline=baseline, refused_out=refused_out,
855
906
  )
856
907
  except ValueError as exc: # intra-target run_gen collision → clean error
857
908
  errors.append(f"target '{t.name}': {exc}")
@@ -918,13 +969,28 @@ def _cmd_gen_neutral_fallback(args: argparse.Namespace) -> int:
918
969
 
919
970
  gen_state = str(root_dir.resolve() / ".metaobjects" / ".gen-state")
920
971
  column_naming = getattr(args, "column_naming", None) or DEFAULT_COLUMN_NAMING
972
+ refused: list[str] = []
921
973
  written = _run_suite(
922
974
  root, args.out, generators, entities,
923
975
  gen_state_dir=gen_state, column_naming=column_naming,
976
+ project_root=str(root_dir.resolve()),
977
+ baseline=getattr(args, "baseline", None) or "default",
978
+ refused_out=refused,
924
979
  )
925
980
  for path in written:
926
981
  print(path)
927
982
  print(f"metaobjects gen: wrote {len(written)} file(s) to {args.out}")
983
+ if refused:
984
+ # A refusal is a failed gate, matching TypeScript's `meta gen` (exit 1). Python and
985
+ # C# used to warn and exit 0, so an adopter wiring this into CI as a drift gate was
986
+ # green while codegen refused to write — a gate that did not gate. The warnings
987
+ # above carry the detail; this line carries the verdict.
988
+ print(
989
+ f"metaobjects gen: FAILED — {len(refused)} file(s) refused (see the warnings "
990
+ f"above; `--baseline=adopt` records what you have and writes nothing)",
991
+ file=sys.stderr,
992
+ )
993
+ return 1
928
994
  return 0
929
995
 
930
996
 
@@ -981,8 +1047,13 @@ def _cmd_gen_config(args: argparse.Namespace) -> int:
981
1047
  print(f" {msg}", file=sys.stderr)
982
1048
  return 1
983
1049
 
1050
+ refused: list[str] = []
984
1051
  written, errors = _run_gen_targets(
985
- config, targets, root, gen_state_dir=gen_state_dir_for(config.metadata_dir())
1052
+ config, targets, root,
1053
+ gen_state_dir=gen_state_dir_for(config.metadata_dir()),
1054
+ project_root=str(project_root_for(config.metadata_dir())),
1055
+ baseline=getattr(args, "baseline", None) or "default",
1056
+ refused_out=refused,
986
1057
  )
987
1058
  if errors:
988
1059
  for msg in errors:
@@ -993,6 +1064,17 @@ def _cmd_gen_config(args: argparse.Namespace) -> int:
993
1064
  print(
994
1065
  f"metaobjects gen: wrote {len(written)} file(s) across {len(targets)} target(s)."
995
1066
  )
1067
+ if refused:
1068
+ # A refusal is a failed gate, matching TypeScript's `meta gen` (exit 1). Python and
1069
+ # C# used to warn and exit 0, so an adopter wiring this into CI as a drift gate was
1070
+ # green while codegen refused to write — a gate that did not gate. The warnings
1071
+ # above carry the detail; this line carries the verdict.
1072
+ print(
1073
+ f"metaobjects gen: FAILED — {len(refused)} file(s) refused (see the warnings "
1074
+ f"above; `--baseline=adopt` records what you have and writes nothing)",
1075
+ file=sys.stderr,
1076
+ )
1077
+ return 1
996
1078
  return 0
997
1079
 
998
1080
 
@@ -1033,7 +1115,22 @@ def _relative_set(root: Path) -> dict[str, str]:
1033
1115
  return files
1034
1116
 
1035
1117
 
1036
- def _is_ours_for(gen_state_dir: str | None):
1118
+ def _out_prefix(out_dir: str, project_root: Path) -> str:
1119
+ """The out dir, relative to the project — the translation between this diff's
1120
+ out-dir-relative keys and the manifest's project-relative ones.
1121
+
1122
+ BOTH SIDES RESOLVED, and that is the whole content of this function. `project_root`
1123
+ arrives resolved; relpath from a resolved anchor to an unresolved path is not the
1124
+ path between the two directories, and under a directory symlink it walks OUT of the
1125
+ project. Every lookup then misses, `is_ours` is false for every file, and the gate
1126
+ prints a clean verdict over stale output — silent in the direction that matters.
1127
+ It is written once here rather than at each of the three call sites, because three
1128
+ spellings of one translation is how they came to disagree in the first place.
1129
+ """
1130
+ return os.path.relpath(Path(out_dir).resolve(), project_root)
1131
+
1132
+
1133
+ def _is_ours_for(gen_state_dir: str | None, out_prefix: str = ""):
1037
1134
  """Jurisdiction predicate for the ``extra`` verdict: did WE write this path?
1038
1135
 
1039
1136
  ``out_dir`` is a DIRECTORY, not a namespace this tool owns — the same ruling the
@@ -1041,20 +1138,36 @@ def _is_ours_for(gen_state_dir: str | None):
1041
1138
  beyond ``*.py`` means the gate now sees every stranger's file too, and convicting
1042
1139
  those turns a project with zero drift into a red build.
1043
1140
 
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.
1141
+ The manifest already records what we WROTE, so it answers the ownership question
1142
+ exactly but it is keyed relative to the PROJECT while this diff is keyed relative
1143
+ to ``out_dir``. ``out_prefix`` (the out dir, relative to the project) is what turns
1144
+ one into the other. Getting this wrong is silent in the direction that matters: a
1145
+ lookup that finds nothing makes ``is_ours`` false for every file, and the gate simply
1146
+ stops reporting stale output while still printing a clean verdict.
1147
+
1148
+ The out-dir-relative name is tried as a fallback, because that is how a manifest
1149
+ written before the re-keying spells it — the same fallback ``decide_and_write``
1150
+ makes, so the write path and the gate cannot disagree about which entry is a record
1151
+ of this file.
1152
+
1046
1153
  FAILS CLOSED, matching :func:`is_pristine_generated`: with no manifest at all
1047
1154
  nothing is proven, so every file stays in scope and the old verdict stands.
1048
1155
  """
1049
1156
  if gen_state_dir is None or not has_hash_manifest(gen_state_dir):
1050
1157
  return lambda _rel: True
1051
- return lambda rel: read_generated_hash(gen_state_dir, rel) is not None
1158
+
1159
+ def is_ours(rel: str) -> bool:
1160
+ key = os.path.join(out_prefix, rel) if out_prefix else rel
1161
+ return read_generated_hash(gen_state_dir, key, legacy_rel_path=rel) is not None
1162
+
1163
+ return is_ours
1052
1164
 
1053
1165
 
1054
1166
  def _diff_report(
1055
1167
  expected: dict[str, str],
1056
1168
  committed: dict[str, str],
1057
1169
  gen_state_dir: str | None = None,
1170
+ out_prefix: str = "",
1058
1171
  ) -> int:
1059
1172
  """Compare a regenerated file map against the committed one; print the
1060
1173
  standard ``verify --codegen`` drift report. Returns 0 (in sync) or 1.
@@ -1065,8 +1178,11 @@ def _diff_report(
1065
1178
 
1066
1179
  ``gen_state_dir`` scopes the ``extra`` verdict to files we have a record of
1067
1180
  writing — see :func:`_is_ours_for`. Omit it to keep the unscoped verdict.
1181
+ ``out_prefix`` is the committed out dir relative to the project, which is what
1182
+ translates this diff's out-dir-relative keys into the manifest's project-relative
1183
+ ones; omit it only when the two coincide.
1068
1184
  """
1069
- is_ours = _is_ours_for(gen_state_dir)
1185
+ is_ours = _is_ours_for(gen_state_dir, out_prefix)
1070
1186
  changed = sorted(
1071
1187
  k for k in expected if k in committed and expected[k] != committed[k]
1072
1188
  )
@@ -1179,7 +1295,14 @@ def _verify_codegen(args: argparse.Namespace) -> int:
1179
1295
  expected = _relative_set(Path(tmp))
1180
1296
  committed = _relative_set(Path(args.out))
1181
1297
 
1182
- return _diff_report(expected, committed, gen_state_dir_for(args.metadata_dir))
1298
+ # The manifest is keyed relative to the project; this diff is keyed relative to
1299
+ # `--out`. Hand over the prefix that translates one into the other, or the
1300
+ # jurisdiction lookup finds nothing and the `extra` verdict silently empties.
1301
+ project_root = project_root_for(args.metadata_dir)
1302
+ return _diff_report(
1303
+ expected, committed, gen_state_dir_for(args.metadata_dir),
1304
+ _out_prefix(args.out, project_root),
1305
+ )
1183
1306
 
1184
1307
 
1185
1308
  def _verify_codegen_neutral_fallback(args: argparse.Namespace) -> int:
@@ -1234,7 +1357,8 @@ def _verify_codegen_neutral_fallback(args: argparse.Namespace) -> int:
1234
1357
  # `_cmd_gen_neutral_fallback` writes its manifest to), so the jurisdiction lookup
1235
1358
  # must be built from that, not from args.metadata_dir (None here by definition).
1236
1359
  return _diff_report(
1237
- expected, committed, str(root_dir.resolve() / ".metaobjects" / ".gen-state")
1360
+ expected, committed, str(root_dir.resolve() / ".metaobjects" / ".gen-state"),
1361
+ _out_prefix(args.out, root_dir.resolve()),
1238
1362
  )
1239
1363
 
1240
1364
 
@@ -1351,7 +1475,14 @@ def _verify_codegen_config(args: argparse.Namespace) -> int:
1351
1475
  # Same jurisdiction rule as _diff_report — this rung inlines its diff to
1352
1476
  # report per-target, so the guard has to be inlined with it. Without it,
1353
1477
  # broadening the comparison past *.py convicts every stranger's file.
1354
- is_ours = _is_ours_for(gen_state_dir_for(config.metadata_dir()))
1478
+ #
1479
+ # PER-TARGET prefix, because that is the whole point on this rung: two
1480
+ # targets with different outDirs share one manifest, and the key that tells
1481
+ # their identically-named files apart is the one anchored on the project.
1482
+ is_ours = _is_ours_for(
1483
+ gen_state_dir_for(config.metadata_dir()),
1484
+ _out_prefix(real_outdir, project_root_for(config.metadata_dir())),
1485
+ )
1355
1486
  extra = sorted(k for k in committed if k not in expected and is_ours(k))
1356
1487
 
1357
1488
  names = "+".join(
@@ -1640,6 +1771,19 @@ def _build_parser() -> argparse.ArgumentParser:
1640
1771
  action="store_true",
1641
1772
  help="list registered generators (stable name + description) and exit",
1642
1773
  )
1774
+ gen.add_argument(
1775
+ "--baseline",
1776
+ choices=("default", "adopt"),
1777
+ default="default",
1778
+ help=(
1779
+ "first-time-on-existing-file behaviour. 'default' refuses a file that cannot "
1780
+ "be proved to be generated output; 'adopt' records the files you have as the "
1781
+ "baseline and writes NOTHING — the one run a project with no committed "
1782
+ ".gen-state/.hashes.json can perform, since nothing writes a manifest until a "
1783
+ "gen succeeds. Adopting DECLARES those files to be generated output: the next "
1784
+ "gen regenerates over them, so commit before you run it."
1785
+ ),
1786
+ )
1643
1787
  gen.add_argument(
1644
1788
  "--template-spec",
1645
1789
  dest="template_spec",
@@ -17,6 +17,22 @@ class GenConfig:
17
17
  # ``<project>/.metaobjects/.gen-state``. COMMIT the manifest: it is the only thing
18
18
  # that makes hand-edit detection work on a machine that did not generate the code.
19
19
  gen_state_dir: str | None = None
20
+ # The project the manifest belongs to — the directory holding ``metaobjects/`` and
21
+ # ``.metaobjects/``. Manifest keys are relative to THIS, not to ``out_dir``.
22
+ #
23
+ # They used to be out-dir-relative while the manifest itself is anchored on the
24
+ # project, so two runs with different ``--out`` shared one manifest keyed by names
25
+ # that mean different files: `gen --out /tmp/a` and `gen --out /tmp/b` both record
26
+ # `alarm_names.py`, and run B's hash then claims ownership of run A's file. The
27
+ # manifest is the only thing that can tell "this is exactly what I wrote" from
28
+ # "somebody edited this" on a machine that did not generate the code, so one that can
29
+ # be populated from an unrelated tree and keyed ambiguously is one that can say yes to
30
+ # the wrong file. TS keys project-root-relative, which is also what makes multiple
31
+ # output targets work.
32
+ #
33
+ # None keeps the out-dir-relative keys, so a programmatic caller that never had a
34
+ # project root is unchanged. The CLI always sets it.
35
+ project_root: str | None = None
20
36
  # Only "flat" is implemented; a non-default value is REFUSED below rather than
21
37
  # silently ignored (see __post_init__).
22
38
  output_layout: str = "flat"
@@ -37,6 +53,12 @@ class GenConfig:
37
53
  # generator emits names a different column than the one a row actually lands
38
54
  # in.
39
55
  column_naming: str = DEFAULT_COLUMN_NAMING
56
+ # First-time-on-existing-file behaviour. "default" refuses a file that cannot be
57
+ # proved to be generated output; "adopt" records what is on disk as the baseline and
58
+ # writes nothing — the one run a project with no committed manifest can perform (the
59
+ # refusal used to prescribe committing a manifest nothing had yet produced). A value
60
+ # this port does not implement is REFUSED in __post_init__ rather than ignored.
61
+ baseline: str = "default"
40
62
  provided_enum_namespace: str | None = None
41
63
  provided_enum_packages: dict[str, str] = field(default_factory=dict)
42
64
 
@@ -64,6 +86,17 @@ class GenConfig:
64
86
  f"GenConfig.output_layout={self.output_layout!r}: only 'flat' is "
65
87
  "implemented in this port."
66
88
  )
89
+ if self.baseline not in ("default", "adopt"):
90
+ # `fresh` is deliberately absent from this port rather than silently accepted:
91
+ # TypeScript implements it (overwrite from fresh + re-baseline) and this port
92
+ # does not, and taking the value while refusing files anyway is the kind of
93
+ # cross-port lie this whole method exists to prevent.
94
+ raise ValueError(
95
+ f"GenConfig.baseline={self.baseline!r}: this port implements 'default' and "
96
+ "'adopt'. To discard a refused file's content here, delete the file and "
97
+ "re-run — it is then written as new. (TypeScript's '--baseline=fresh' does "
98
+ "that in one step; it is not implemented in this port.)"
99
+ )
67
100
  if self.emit_abstract_shapes is not True:
68
101
  raise ValueError(
69
102
  "GenConfig.emit_abstract_shapes=False: this port always emits the abstract "
@@ -0,0 +1,72 @@
1
+ """Canonicalize emitted source via ruff: sort imports (isort), then format.
2
+
3
+ Both passes read stdin / write stdout, so generated code is import-sorted AND
4
+ formatted — i.e. `ruff check`- and `ruff format`-clean for downstream consumers."""
5
+ from __future__ import annotations
6
+
7
+ import importlib.util
8
+ import subprocess
9
+ import sys
10
+
11
+ _STDIN_NAME = "generated.py"
12
+
13
+ #: ruff is a codegen-time convenience (import-sort + format), not a runtime requirement
14
+ #: of the emitted code. When it's absent we emit valid-but-unformatted Python rather
15
+ #: than hard-failing the whole ``gen``.
16
+ _RUFF_AVAILABLE = importlib.util.find_spec("ruff") is not None
17
+
18
+
19
+ def _run_ruff(args: list[str], source: str) -> str:
20
+ proc = subprocess.run(
21
+ [sys.executable, "-m", "ruff", *args],
22
+ input=source,
23
+ capture_output=True,
24
+ text=True,
25
+ )
26
+ if proc.returncode != 0:
27
+ # A non-zero exit is read by the caller as "ruff rejected the source" — a
28
+ # generator bug. Two things make that reading unsafe, so record what actually
29
+ # happened instead of asserting a cause.
30
+ #
31
+ # First, ruff reports a source it cannot parse as exit 2 WITH stderr (measured,
32
+ # ruff 0.15.14: `error: Failed to parse at 1:7: ...`). So an EMPTY stderr is not
33
+ # a source rejection — the process did not get far enough to say anything.
34
+ # Second, a NEGATIVE returncode is death by signal, which is not a verdict on
35
+ # the source at all.
36
+ #
37
+ # This branch used to raise `ruff format failed:` — trailing off after the colon
38
+ # whenever stderr was empty, naming neither the exit status nor the fact that
39
+ # ruff never looked at the source, and sending a reader hunting a syntax error
40
+ # that did not exist. Always carry the status; never claim a source error for a
41
+ # process that did not finish, and do not guess at WHY it did not finish.
42
+ detail = proc.stderr.strip()
43
+ if proc.returncode < 0:
44
+ raise RuntimeError(
45
+ f"ruff {args[0]} was killed by signal {-proc.returncode} before it could "
46
+ f"format the source — this is not a source error; the formatter never ran "
47
+ f"to completion" + (f": {detail}" if detail else "")
48
+ )
49
+ raise RuntimeError(
50
+ f"ruff {args[0]} failed (exit {proc.returncode})"
51
+ + (
52
+ f": {detail}"
53
+ if detail
54
+ else " and wrote nothing to stderr — ruff reports an unparseable source"
55
+ " WITH stderr, so this is not a source error"
56
+ )
57
+ )
58
+ return proc.stdout
59
+
60
+
61
+ def ruff_format(source: str) -> str:
62
+ """Sort imports then format *source*; return the canonical text.
63
+
64
+ A no-op (returns *source* unchanged) when ruff is not installed — the emitted
65
+ code is still valid Python, just unformatted. Still raises RuntimeError if ruff
66
+ IS present but fails (e.g. a syntax error in emitted code — that's a generator bug)."""
67
+ if not _RUFF_AVAILABLE:
68
+ return source
69
+ sorted_src = _run_ruff(
70
+ ["check", "--select", "I", "--fix", "--stdin-filename", _STDIN_NAME, "-"], source
71
+ )
72
+ return _run_ruff(["format", "-"], sorted_src)