metaobjects 0.22.1__tar.gz → 0.23.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 (408) hide show
  1. {metaobjects-0.22.1 → metaobjects-0.23.0}/PKG-INFO +1 -1
  2. {metaobjects-0.22.1 → metaobjects-0.23.0}/pyproject.toml +1 -1
  3. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/core_types.py +44 -8
  4. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/requirement/meta_requirement.py +44 -8
  5. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/requirement/requirement_constants.py +31 -0
  6. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/documentation.json +4 -4
  7. metaobjects-0.23.0/src/metaobjects/spec_metamodel/requirement.json +209 -0
  8. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/postgres_container.py +84 -14
  9. metaobjects-0.22.1/src/metaobjects/spec_metamodel/requirement.json +0 -140
  10. {metaobjects-0.22.1 → metaobjects-0.23.0}/.gitignore +0 -0
  11. {metaobjects-0.22.1 → metaobjects-0.23.0}/LICENSE +0 -0
  12. {metaobjects-0.22.1 → metaobjects-0.23.0}/README.md +0 -0
  13. {metaobjects-0.22.1 → metaobjects-0.23.0}/hatch_build.py +0 -0
  14. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/__init__.py +0 -0
  15. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/agent_context/__init__.py +0 -0
  16. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/agent_context/scaffold.py +0 -0
  17. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/apidocs/__init__.py +0 -0
  18. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/apidocs/api_model.py +0 -0
  19. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/apidocs/builder.py +0 -0
  20. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/apidocs/naming.py +0 -0
  21. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/apidocs/paths.py +0 -0
  22. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/apidocs/renderer.py +0 -0
  23. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/attr_class_map.py +0 -0
  24. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/cli.py +0 -0
  25. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/KNOWN_GAPS.md +0 -0
  26. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/__init__.py +0 -0
  27. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/collision_names.py +0 -0
  28. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/config.py +0 -0
  29. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/constants.py +0 -0
  30. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/extract_delegate_emitter.py +0 -0
  31. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/extract_schema_emitter.py +0 -0
  32. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/format.py +0 -0
  33. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/fr010_field_mapping.py +0 -0
  34. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generator.py +0 -0
  35. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generator_registry.py +0 -0
  36. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/__init__.py +0 -0
  37. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/entity_model.py +0 -0
  38. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/extractor_generator.py +0 -0
  39. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/filter_allowlist_generator.py +0 -0
  40. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/fr019_shared_enum.py +0 -0
  41. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/m2m_codegen.py +0 -0
  42. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/output_parser_generator.py +0 -0
  43. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/output_prompt_generator.py +0 -0
  44. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/payload_vo_generator.py +0 -0
  45. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/render_helper_generator.py +0 -0
  46. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/router_generator.py +0 -0
  47. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/template_generator.py +0 -0
  48. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/tph_plan.py +0 -0
  49. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/generators/trace_helper_generator.py +0 -0
  50. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/instance_artifacts.py +0 -0
  51. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/metaobjects-config.schema.json +0 -0
  52. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/output_format_spec_emitter.py +0 -0
  53. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/overwrite_policy.py +0 -0
  54. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/project_config.py +0 -0
  55. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/runner.py +0 -0
  56. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/runtime/__init__.py +0 -0
  57. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/runtime/filter_parser.py +0 -0
  58. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/template_codegen/__init__.py +0 -0
  59. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/template_codegen/output_pattern.py +0 -0
  60. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/template_codegen/template_data.py +0 -0
  61. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/template_codegen/template_spec.py +0 -0
  62. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/codegen/type_map.py +0 -0
  63. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/datatype.py +0 -0
  64. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/documentation/__init__.py +0 -0
  65. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/documentation/doc_constants.py +0 -0
  66. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/documentation/doc_provider.py +0 -0
  67. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/documentation/doc_schema.py +0 -0
  68. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/errors.py +0 -0
  69. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/__init__.py +0 -0
  70. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/merge.py +0 -0
  71. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/meta_data_loader.py +0 -0
  72. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/registered_validation.py +0 -0
  73. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/sources/__init__.py +0 -0
  74. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/sources/directory_source.py +0 -0
  75. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/sources/file_source.py +0 -0
  76. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/sources/meta_data_source.py +0 -0
  77. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/sources/uri_source.py +0 -0
  78. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/validate_discriminator.py +0 -0
  79. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/validate_enum_normalize_ambiguity.py +0 -0
  80. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/validate_field_readonly.py +0 -0
  81. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/validate_source_escapes.py +0 -0
  82. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/validate_source_parameter_ref.py +0 -0
  83. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/validate_source_physical_names.py +0 -0
  84. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/loader/validation_passes.py +0 -0
  85. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/__init__.py +0 -0
  86. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/__init__.py +0 -0
  87. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/attr/__init__.py +0 -0
  88. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/attr/attr_constants.py +0 -0
  89. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/attr/meta_attr.py +0 -0
  90. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/field/__init__.py +0 -0
  91. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/field/field_constants.py +0 -0
  92. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/field/meta_field.py +0 -0
  93. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/identity/__init__.py +0 -0
  94. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/identity/identity_constants.py +0 -0
  95. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/identity/meta_identity.py +0 -0
  96. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/index/__init__.py +0 -0
  97. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/index/index_constants.py +0 -0
  98. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/index/meta_index.py +0 -0
  99. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/object/__init__.py +0 -0
  100. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/object/meta_object.py +0 -0
  101. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/object/meta_object_aware.py +0 -0
  102. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/object/object_class_registry.py +0 -0
  103. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/object/object_constants.py +0 -0
  104. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/object/object_extract.py +0 -0
  105. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/object/value_object.py +0 -0
  106. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/relationship/__init__.py +0 -0
  107. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/relationship/derive_m2m_fields.py +0 -0
  108. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/relationship/meta_relationship.py +0 -0
  109. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/relationship/relationship_constants.py +0 -0
  110. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/requirement/__init__.py +0 -0
  111. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/validator/__init__.py +0 -0
  112. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/core/validator/validator_constants.py +0 -0
  113. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/meta_data.py +0 -0
  114. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/meta_root.py +0 -0
  115. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/__init__.py +0 -0
  116. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/db/__init__.py +0 -0
  117. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/db/db_constants.py +0 -0
  118. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/db/db_provider.py +0 -0
  119. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/origin/__init__.py +0 -0
  120. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/origin/meta_origin.py +0 -0
  121. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/origin/origin_constants.py +0 -0
  122. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/source/__init__.py +0 -0
  123. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/source/meta_source.py +0 -0
  124. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/persistence/source/source_constants.py +0 -0
  125. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/__init__.py +0 -0
  126. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/layout/__init__.py +0 -0
  127. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/layout/layout_constants.py +0 -0
  128. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/layout/meta_layout.py +0 -0
  129. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/ui/__init__.py +0 -0
  130. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/ui/ui_provider.py +0 -0
  131. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/view/__init__.py +0 -0
  132. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/view/meta_view.py +0 -0
  133. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/presentation/view/view_constants.py +0 -0
  134. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/provider_extends.py +0 -0
  135. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/template/__init__.py +0 -0
  136. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/template/meta_template.py +0 -0
  137. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/template/prompt_provider.py +0 -0
  138. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/meta/template/template_constants.py +0 -0
  139. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/naming_refs.py +0 -0
  140. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/parser.py +0 -0
  141. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/parser_yaml.py +0 -0
  142. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/provider.py +0 -0
  143. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/py.typed +0 -0
  144. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/registry.py +0 -0
  145. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/registry_manifest.py +0 -0
  146. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/__init__.py +0 -0
  147. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/email_document.py +0 -0
  148. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/escapers.py +0 -0
  149. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/KNOWN_GAPS.md +0 -0
  150. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/__init__.py +0 -0
  151. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/coerce.py +0 -0
  152. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/extract.py +0 -0
  153. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/extract_map.py +0 -0
  154. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/json_forgiving_reader.py +0 -0
  155. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/locate.py +0 -0
  156. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/normalize.py +0 -0
  157. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/strip.py +0 -0
  158. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/types.py +0 -0
  159. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/extract/xml_forgiving_reader.py +0 -0
  160. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/filesystem_provider.py +0 -0
  161. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/prompt/__init__.py +0 -0
  162. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/prompt/output_format_renderer.py +0 -0
  163. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/prompt/output_format_spec.py +0 -0
  164. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/prompt/prompt_field.py +0 -0
  165. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/prompt/prompt_overrides.py +0 -0
  166. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/prompt/prompt_style.py +0 -0
  167. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/renderer.py +0 -0
  168. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/render/verify.py +0 -0
  169. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/runtime/__init__.py +0 -0
  170. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/runtime/llm_recorder.py +0 -0
  171. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/runtime/n2m_resolver.py +0 -0
  172. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/runtime/object_manager.py +0 -0
  173. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/runtime/tph.py +0 -0
  174. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/serializer_json.py +0 -0
  175. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/shared/__init__.py +0 -0
  176. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/shared/base_types.py +0 -0
  177. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/shared/separators.py +0 -0
  178. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/shared/structural.py +0 -0
  179. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/source/__init__.py +0 -0
  180. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/source/error_source.py +0 -0
  181. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/source/json_path.py +0 -0
  182. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/source/semantic_diff.py +0 -0
  183. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/source/yaml_positions.py +0 -0
  184. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/__init__.py +0 -0
  185. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/attr.json +0 -0
  186. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/db.json +0 -0
  187. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/field.json +0 -0
  188. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/identity.json +0 -0
  189. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/index.json +0 -0
  190. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/layout.json +0 -0
  191. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/object.json +0 -0
  192. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/origin.json +0 -0
  193. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/prompt.json +0 -0
  194. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/relationship.json +0 -0
  195. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/source.json +0 -0
  196. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/template.json +0 -0
  197. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/ui-web.json +0 -0
  198. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/ui.json +0 -0
  199. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/validator.json +0 -0
  200. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/spec_metamodel/view.json +0 -0
  201. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/super_resolve.py +0 -0
  202. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/validation_types.py +0 -0
  203. {metaobjects-0.22.1 → metaobjects-0.23.0}/src/metaobjects/yaml_desugar.py +0 -0
  204. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/__init__.py +0 -0
  205. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/__init__.py +0 -0
  206. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/extends/expected/BaseEntity.py +0 -0
  207. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/extends/expected/Program.py +0 -0
  208. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/extends/meta.json +0 -0
  209. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/nested-array/expected/AuthorBrief.py +0 -0
  210. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/nested-array/expected/PostBrief.py +0 -0
  211. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/nested-array/meta.json +0 -0
  212. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/scalars/expected/Metric.py +0 -0
  213. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/scalars/expected/Report.py +0 -0
  214. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/scalars/meta.json +0 -0
  215. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/vanilla/expected/Subscriber.py +0 -0
  216. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/golden/vanilla/meta.json +0 -0
  217. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_abstract_conformance.py +0 -0
  218. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_api_docs_builder.py +0 -0
  219. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_api_docs_paths.py +0 -0
  220. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_cli.py +0 -0
  221. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_cli_config_gen.py +0 -0
  222. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_cli_config_verify.py +0 -0
  223. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_cli_providers.py +0 -0
  224. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_cli_registry.py +0 -0
  225. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_cli_staleness_nudge.py +0 -0
  226. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_cli_verify_strict.py +0 -0
  227. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_cli_verify_subverbs.py +0 -0
  228. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_constants_config.py +0 -0
  229. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_entity_model.py +0 -0
  230. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_enum_conformance.py +0 -0
  231. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_extends_abstract_field_inheritance.py +0 -0
  232. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_extract_tier_collision.py +0 -0
  233. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_extractor_generator.py +0 -0
  234. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_filter_allowlist_generator.py +0 -0
  235. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_format.py +0 -0
  236. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_fr010_nested_object_prompt.py +0 -0
  237. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_fr010_output_codegen.py +0 -0
  238. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_fr019_shared_provided_conformance.py +0 -0
  239. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_generator.py +0 -0
  240. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_generator_extension_seams.py +0 -0
  241. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_golden.py +0 -0
  242. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_inherit_without_restate_gate.py +0 -0
  243. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_inheritance_conformance.py +0 -0
  244. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_instance_artifacts.py +0 -0
  245. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_m2m_codegen.py +0 -0
  246. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_origin_nullability.py +0 -0
  247. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_output_parser_generator.py +0 -0
  248. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_overwrite_policy.py +0 -0
  249. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_payload_vo_generator.py +0 -0
  250. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_project_config.py +0 -0
  251. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_projection_compile.py +0 -0
  252. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_render_helper_conformance.py +0 -0
  253. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_render_helper_generator.py +0 -0
  254. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_reverse_finders.py +0 -0
  255. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_router_autoset.py +0 -0
  256. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_router_generator.py +0 -0
  257. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_runner.py +0 -0
  258. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_sourceless_projection.py +0 -0
  259. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_template_data.py +0 -0
  260. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_template_generator.py +0 -0
  261. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_template_output_pattern.py +0 -0
  262. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_template_scope_helpers.py +0 -0
  263. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_template_scope_walk.py +0 -0
  264. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_template_spec.py +0 -0
  265. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_tph_codegen.py +0 -0
  266. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_trace_helper_generator.py +0 -0
  267. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_type_map.py +0 -0
  268. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_validation_conformance.py +0 -0
  269. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/codegen/test_write_through.py +0 -0
  270. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/__init__.py +0 -0
  271. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/capabilities.py +0 -0
  272. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/conformance-expected-failures.json +0 -0
  273. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/conformance_adapter.py +0 -0
  274. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/corpus.py +0 -0
  275. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/expected_failures.py +0 -0
  276. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/fixture_discovery.py +0 -0
  277. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/navigator.py +0 -0
  278. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_api_docs_cross_port_conformance.py +0 -0
  279. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_conformance.py +0 -0
  280. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_extract_conformance.py +0 -0
  281. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_extract_object_verdict.py +0 -0
  282. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_fr010_loader_attrs.py +0 -0
  283. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_fr011_attrs.py +0 -0
  284. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_generator_registry_conformance.py +0 -0
  285. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_object_model_conformance.py +0 -0
  286. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_provider_composition_conformance.py +0 -0
  287. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_registry_conformance.py +0 -0
  288. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_runner_hardfail.py +0 -0
  289. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_spec_metamodel_embed.py +0 -0
  290. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_strict_attr_load.py +0 -0
  291. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_template_codegen_conformance.py +0 -0
  292. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_template_generator_conformance.py +0 -0
  293. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/test_yaml_conformance.py +0 -0
  294. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/conformance/yaml-conformance-expected-failures.json +0 -0
  295. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/__init__.py +0 -0
  296. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/api_contract_assertions.py +0 -0
  297. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/api_contract_jsonb_server.py +0 -0
  298. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/api_contract_m2m_server.py +0 -0
  299. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/api_contract_server.py +0 -0
  300. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/generated_jsonb_app.py +0 -0
  301. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/generated_m2m_app.py +0 -0
  302. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/generated_router_app.py +0 -0
  303. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/generated_tph_app.py +0 -0
  304. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/meta_ai_trace.yaml +0 -0
  305. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/normalization.py +0 -0
  306. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/query_runner.py +0 -0
  307. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/scenarios.py +0 -0
  308. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_api_contract.py +0 -0
  309. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_api_contract_generated.py +0 -0
  310. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_api_contract_jsonb.py +0 -0
  311. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_api_contract_m2m.py +0 -0
  312. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_api_contract_m2m_generated.py +0 -0
  313. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_api_contract_tph_generated.py +0 -0
  314. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_generated_router_uuid_pk.py +0 -0
  315. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_llm_call_trace.py +0 -0
  316. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_normalization.py +0 -0
  317. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_query_scenarios.py +0 -0
  318. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/integration/test_runtime_return_types.py +0 -0
  319. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/loader/__init__.py +0 -0
  320. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/loader/test_enum_extends_values_conflict.py +0 -0
  321. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/loader/test_template_payload_target_210.py +0 -0
  322. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/open_closed_proof_test.py +0 -0
  323. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/__init__.py +0 -0
  324. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/__init__.py +0 -0
  325. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_coerce.py +0 -0
  326. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_extract.py +0 -0
  327. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_extract_map.py +0 -0
  328. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_json_forgiving_reader.py +0 -0
  329. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_locate.py +0 -0
  330. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_model.py +0 -0
  331. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_normalize.py +0 -0
  332. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_strip.py +0 -0
  333. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/extract/test_xml_forgiving_reader.py +0 -0
  334. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/prompt/__init__.py +0 -0
  335. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/prompt/test_output_format_renderer.py +0 -0
  336. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/test_email_document.py +0 -0
  337. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/test_filesystem_provider.py +0 -0
  338. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/test_output_format_renderer_nested.py +0 -0
  339. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/test_output_prompt_conformance.py +0 -0
  340. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/test_render_conformance.py +0 -0
  341. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/test_render_max_chars.py +0 -0
  342. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/test_verify.py +0 -0
  343. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/render/test_verify_conformance.py +0 -0
  344. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/source/__init__.py +0 -0
  345. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/source/test_fr5c_merge_attribution.py +0 -0
  346. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/source/test_fr5d_reference_resolution.py +0 -0
  347. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/source/test_fr5e_database_source_shape.py +0 -0
  348. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/source/test_json_path.py +0 -0
  349. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/source/test_semantic_diff.py +0 -0
  350. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/source/test_source_on_node.py +0 -0
  351. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/source/test_yaml_positions.py +0 -0
  352. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/test_api_docs_accuracy.py +0 -0
  353. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/__init__.py +0 -0
  354. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_agent_context_staleness.py +0 -0
  355. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_capabilities.py +0 -0
  356. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_common_attrs.py +0 -0
  357. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_core_types.py +0 -0
  358. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_effective_package.py +0 -0
  359. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_errors.py +0 -0
  360. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_field_enum.py +0 -0
  361. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_field_map_validation.py +0 -0
  362. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_field_uuid_dbcolumntype.py +0 -0
  363. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_fr016_source_name_and_kind_aliases.py +0 -0
  364. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_index_lookup.py +0 -0
  365. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_llm_recorder.py +0 -0
  366. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_loader.py +0 -0
  367. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_loader_bom.py +0 -0
  368. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_loader_class.py +0 -0
  369. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_merge.py +0 -0
  370. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_meta_attr.py +0 -0
  371. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_meta_data.py +0 -0
  372. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_meta_source.py +0 -0
  373. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_module_shortcuts.py +0 -0
  374. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_n2m_resolver.py +0 -0
  375. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_n2m_resolver_inherited.py +0 -0
  376. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_object_manager_autoset_stamping.py +0 -0
  377. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_object_manager_uuid_coercion.py +0 -0
  378. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_object_manager_write_through.py +0 -0
  379. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_one_primary_source.py +0 -0
  380. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_parser.py +0 -0
  381. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_parser_yaml.py +0 -0
  382. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_provider.py +0 -0
  383. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_provider_extension.py +0 -0
  384. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_registry.py +0 -0
  385. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_registry_completeness.py +0 -0
  386. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_registry_extend.py +0 -0
  387. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_registry_sealed.py +0 -0
  388. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_relationship_referential_actions.py +0 -0
  389. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_resolution_key.py +0 -0
  390. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_runtime_resolution_key_binding.py +0 -0
  391. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_serializer.py +0 -0
  392. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_shared_constants.py +0 -0
  393. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_smoke.py +0 -0
  394. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_source_escapes.py +0 -0
  395. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_sources.py +0 -0
  396. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_strict_child_placement.py +0 -0
  397. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_super_resolve.py +0 -0
  398. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_template_toolcall.py +0 -0
  399. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_template_wrong_subtype_attrs.py +0 -0
  400. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_validation_attr_schema.py +0 -0
  401. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_validation_filter_values.py +0 -0
  402. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_validation_origin_195.py +0 -0
  403. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_validation_origin_paths.py +0 -0
  404. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_validation_sort_field.py +0 -0
  405. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_validation_warnings.py +0 -0
  406. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_write_coercion_arrays.py +0 -0
  407. {metaobjects-0.22.1 → metaobjects-0.23.0}/tests/unit/test_yaml_desugar.py +0 -0
  408. {metaobjects-0.22.1 → metaobjects-0.23.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: metaobjects
3
- Version: 0.22.1
3
+ Version: 0.23.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.22.1"
3
+ version = "0.23.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"
@@ -64,13 +64,16 @@ from .meta.core.object.object_constants import (
64
64
  from .meta.core.relationship.meta_relationship import MetaRelationship
65
65
  from .meta.core.requirement.meta_requirement import MetaRequirement
66
66
  from .meta.core.requirement.requirement_constants import (
67
+ REQUIREMENT_ATTR_DISPOSITION,
67
68
  REQUIREMENT_ATTR_IMPLEMENTED_BY,
68
69
  REQUIREMENT_ATTR_LEVEL,
69
70
  REQUIREMENT_ATTR_STATEMENT,
70
71
  REQUIREMENT_ATTR_STATUS,
71
72
  REQUIREMENT_ATTR_SUPERSEDED_BY,
73
+ REQUIREMENT_ATTR_TRACKED_BY,
72
74
  REQUIREMENT_ATTR_VERIFIED_BY,
73
75
  REQUIREMENT_ATTR_VIOLATION,
76
+ REQUIREMENT_DISPOSITIONS,
74
77
  REQUIREMENT_STATUSES,
75
78
  REQUIREMENT_SUBTYPE_ARCHITECTURAL,
76
79
  REQUIREMENT_SUBTYPE_FUNCTIONAL,
@@ -589,6 +592,23 @@ _REQUIREMENT_COMMON_ATTRS = [
589
592
  required=True,
590
593
  allowed_values=REQUIREMENT_STATUSES,
591
594
  ),
595
+ # What was DECIDED about the outstanding work -- a different question from
596
+ # whether the work is done, which is what @status answers. ABSENT means
597
+ # UNDECIDED, a real state and the one a review exists to find.
598
+ AttrSchema(
599
+ name=REQUIREMENT_ATTR_DISPOSITION,
600
+ value_type=ATTR_SUBTYPE_STRING,
601
+ required=False,
602
+ allowed_values=REQUIREMENT_DISPOSITIONS,
603
+ ),
604
+ # Issue/ticket references for outstanding work. Free-form and never resolved:
605
+ # verify has no network, so unlike @verifiedBy nothing here is checked to exist.
606
+ AttrSchema(
607
+ name=REQUIREMENT_ATTR_TRACKED_BY,
608
+ value_type=ATTR_SUBTYPE_STRING,
609
+ required=False,
610
+ is_array=True,
611
+ ),
592
612
  AttrSchema(name=REQUIREMENT_ATTR_STATEMENT, value_type=ATTR_SUBTYPE_STRING, required=True),
593
613
  AttrSchema(name=REQUIREMENT_ATTR_VIOLATION, value_type=ATTR_SUBTYPE_STRING, required=True),
594
614
  AttrSchema(
@@ -597,6 +617,12 @@ _REQUIREMENT_COMMON_ATTRS = [
597
617
  required=False,
598
618
  is_array=True,
599
619
  ),
620
+ AttrSchema(
621
+ name=REQUIREMENT_ATTR_VERIFIED_BY,
622
+ value_type=ATTR_SUBTYPE_STRING,
623
+ required=False,
624
+ is_array=True,
625
+ ),
600
626
  AttrSchema(name=REQUIREMENT_ATTR_SUPERSEDED_BY, value_type=ATTR_SUBTYPE_STRING, required=False),
601
627
  ]
602
628
  core_provider.add(
@@ -605,14 +631,10 @@ core_provider.add(
605
631
  sub_type=REQUIREMENT_SUBTYPE_FUNCTIONAL,
606
632
  factory=MetaRequirement,
607
633
  attrs=[
634
+ # REQUIRED on functional: levels are the object-in-focus decomposition
635
+ # and a functional requirement always sits at one.
608
636
  AttrSchema(name=REQUIREMENT_ATTR_LEVEL, value_type=ATTR_SUBTYPE_INT, required=True),
609
637
  *_REQUIREMENT_COMMON_ATTRS,
610
- AttrSchema(
611
- name=REQUIREMENT_ATTR_VERIFIED_BY,
612
- value_type=ATTR_SUBTYPE_STRING,
613
- required=False,
614
- is_array=True,
615
- ),
616
638
  ],
617
639
  child_rules=[
618
640
  ChildRule(TYPE_ATTR, "*"),
@@ -626,8 +648,22 @@ core_provider.add(
626
648
  type=TYPE_REQUIREMENT,
627
649
  sub_type=REQUIREMENT_SUBTYPE_ARCHITECTURAL,
628
650
  factory=MetaRequirement,
629
- attrs=list(_REQUIREMENT_COMMON_ATTRS),
630
- child_rules=[ChildRule(TYPE_ATTR, "*")],
651
+ attrs=[
652
+ # OPTIONAL on architectural: absent means a flat, object-independent
653
+ # policy (the original and still the default form); present opts the
654
+ # node into a levelled tree, e.g. a quality taxonomy over the
655
+ # non-functional set. Opt-in precisely so that adding a taxonomy over
656
+ # existing flat policies cannot invalidate them.
657
+ AttrSchema(name=REQUIREMENT_ATTR_LEVEL, value_type=ATTR_SUBTYPE_INT, required=False),
658
+ *_REQUIREMENT_COMMON_ATTRS,
659
+ ],
660
+ child_rules=[
661
+ ChildRule(TYPE_ATTR, "*"),
662
+ # BOTH subtypes nest. Declaring this on `functional` only was an
663
+ # omission, not a design: it made an architectural node nestable under
664
+ # a FUNCTIONAL parent but never under another architectural one.
665
+ ChildRule(TYPE_REQUIREMENT, "*"),
666
+ ],
631
667
  )
632
668
  )
633
669
 
@@ -9,12 +9,16 @@ from __future__ import annotations
9
9
 
10
10
  from ...meta_data import MetaData
11
11
  from .requirement_constants import (
12
+ REQUIREMENT_ATTR_DISPOSITION,
12
13
  REQUIREMENT_ATTR_IMPLEMENTED_BY,
13
14
  REQUIREMENT_ATTR_LEVEL,
14
15
  REQUIREMENT_ATTR_STATUS,
16
+ REQUIREMENT_ATTR_TRACKED_BY,
15
17
  REQUIREMENT_ATTR_VERIFIED_BY,
16
18
  REQUIREMENT_LINK_FLOOR_LEVEL,
19
+ REQUIREMENT_STATUS_PLANNED,
17
20
  REQUIREMENT_STATUSES_REQUIRING_LIVE_NODES,
21
+ REQUIREMENT_STATUSES_WITH_OUTSTANDING_WORK,
18
22
  REQUIREMENT_SUBTYPE_ARCHITECTURAL,
19
23
  REQUIREMENT_SUBTYPE_FUNCTIONAL,
20
24
  )
@@ -31,8 +35,9 @@ class MetaRequirement(MetaData):
31
35
 
32
36
  def level(self) -> int | None:
33
37
  """ADR-0039: resolving. 1 solution · 2 segment · 3 service · 4 object ·
34
- 5 member. Architectural requirements carry none — they are
35
- object-independent by definition."""
38
+ 5 member. REQUIRED on functional; OPTIONAL on architectural, where absent
39
+ means a flat object-independent policy and present opts the node into a
40
+ levelled tree (e.g. a quality taxonomy over the non-functional set)."""
36
41
  v = self.get_meta_attr(REQUIREMENT_ATTR_LEVEL)
37
42
  return v if isinstance(v, int) and not isinstance(v, bool) else None
38
43
 
@@ -53,15 +58,46 @@ class MetaRequirement(MetaData):
53
58
  v = self.get_meta_attr(REQUIREMENT_ATTR_VERIFIED_BY)
54
59
  return [str(x) for x in v] if isinstance(v, list) else []
55
60
 
61
+ def disposition(self) -> str | None:
62
+ """ADR-0039: resolving. What was DECIDED about the outstanding work — a
63
+ different question from whether the work is done, which is what
64
+ ``status`` answers. ``None`` means UNDECIDED, a real state and the one a
65
+ review exists to find."""
66
+ v = self.get_meta_attr(REQUIREMENT_ATTR_DISPOSITION)
67
+ return v if isinstance(v, str) else None
68
+
69
+ def tracked_by(self) -> list[str]:
70
+ """ADR-0039: resolving. Issue/ticket references for outstanding work.
71
+ Free-form and NEVER resolved — verify has no network, so unlike
72
+ ``verified_by`` nothing here is checked to exist."""
73
+ v = self.get_meta_attr(REQUIREMENT_ATTR_TRACKED_BY)
74
+ return [str(x) for x in v] if isinstance(v, list) else []
75
+
76
+ def is_planned(self) -> bool:
77
+ """Intended but not built. Its nodes may legitimately not exist yet, and
78
+ it must NOT count toward object coverage — planning a capability cannot
79
+ be allowed to silence the warning that nothing implements it."""
80
+ return self.status() == REQUIREMENT_STATUS_PLANNED
81
+
82
+ def has_outstanding_work(self) -> bool:
83
+ """True when there is outstanding work, so a ``disposition`` says
84
+ something."""
85
+ s = self.status()
86
+ return s is not None and s in REQUIREMENT_STATUSES_WITH_OUTSTANDING_WORK
87
+
56
88
  def may_reference_model(self) -> bool:
57
89
  """True when this requirement is permitted to reference the model at all.
58
- Architectural requirements always may (their claim set is the point);
59
- functional ones only at or below the link floor, so the organisational
60
- tiers stay organisational."""
61
- if self.is_architectural():
62
- return True
90
+
91
+ An UNLEVELLED architectural requirement always may — its claim set is the
92
+ whole point, and that is the original flat form. Once a level is PRESENT
93
+ the node has opted into a tree, and the link floor applies to it exactly
94
+ as it does to a functional one, so an "ISO 25010 Security" grouping node
95
+ cannot quietly start naming entities. Levelling is the opt-in; enforcing
96
+ the floor unconditionally would have broken every existing flat policy."""
63
97
  lvl = self.level()
64
- return lvl is not None and lvl >= REQUIREMENT_LINK_FLOOR_LEVEL
98
+ if lvl is None:
99
+ return self.is_architectural()
100
+ return lvl >= REQUIREMENT_LINK_FLOOR_LEVEL
65
101
 
66
102
  def requires_live_nodes(self) -> bool:
67
103
  """True when a dangling ``@implementedBy`` is an ERROR rather than
@@ -26,6 +26,8 @@ REQUIREMENT_SUBTYPES = (
26
26
  #: 1 solution · 2 segment (app/library) · 3 service · 4 object · 5 member.
27
27
  REQUIREMENT_ATTR_LEVEL = "level"
28
28
  REQUIREMENT_ATTR_STATUS = "status"
29
+ REQUIREMENT_ATTR_DISPOSITION = "disposition"
30
+ REQUIREMENT_ATTR_TRACKED_BY = "trackedBy"
29
31
  REQUIREMENT_ATTR_STATEMENT = "statement"
30
32
  REQUIREMENT_ATTR_VIOLATION = "violation"
31
33
  REQUIREMENT_ATTR_IMPLEMENTED_BY = "implementedBy"
@@ -35,12 +37,17 @@ REQUIREMENT_ATTR_SUPERSEDED_BY = "supersededBy"
35
37
  # ---------------------------------------------------------------------------
36
38
  # Status — a closed enum, enforced by the registry via allowed_values.
37
39
  # ---------------------------------------------------------------------------
40
+ # Intended but not built. Its references may legitimately dangle, and it never
41
+ # contributes to object coverage -- planning a capability must not silence the
42
+ # warning that nothing implements it.
43
+ REQUIREMENT_STATUS_PLANNED = "planned"
38
44
  REQUIREMENT_STATUS_LIVE = "live"
39
45
  REQUIREMENT_STATUS_PARTIAL = "partial"
40
46
  REQUIREMENT_STATUS_ABANDONED = "abandoned"
41
47
  REQUIREMENT_STATUS_SUPERSEDED = "superseded"
42
48
 
43
49
  REQUIREMENT_STATUSES = (
50
+ REQUIREMENT_STATUS_PLANNED,
44
51
  REQUIREMENT_STATUS_LIVE,
45
52
  REQUIREMENT_STATUS_PARTIAL,
46
53
  REQUIREMENT_STATUS_ABANDONED,
@@ -59,6 +66,30 @@ REQUIREMENT_STATUSES_REQUIRING_LIVE_NODES = (
59
66
  # ---------------------------------------------------------------------------
60
67
  # Levels — organisational above the link floor, model-referencing at or below.
61
68
  # ---------------------------------------------------------------------------
69
+ # Statuses with outstanding work, so a @disposition is meaningful on them. On any
70
+ # other status the decision IS the status, and recording a second one can only
71
+ # agree with it or contradict it.
72
+ REQUIREMENT_STATUSES_WITH_OUTSTANDING_WORK = (
73
+ REQUIREMENT_STATUS_PLANNED,
74
+ REQUIREMENT_STATUS_PARTIAL,
75
+ )
76
+
77
+ # ---------------------------------------------------------------------------
78
+ # Disposition -- what was DECIDED about the outstanding work. Orthogonal to
79
+ # status, which says whether the work is done. ABSENT means UNDECIDED, and that
80
+ # is the state a review exists to find; collapsing it into the status enum would
81
+ # make "there is a gap" and "we chose to live with it" the same fact.
82
+ # ---------------------------------------------------------------------------
83
+
84
+ REQUIREMENT_DISPOSITION_ACCEPTED = "accepted"
85
+ REQUIREMENT_DISPOSITION_DEFERRED = "deferred"
86
+
87
+ # Declaration order is contractual -- see REQUIREMENT_STATUSES.
88
+ REQUIREMENT_DISPOSITIONS = (
89
+ REQUIREMENT_DISPOSITION_ACCEPTED,
90
+ REQUIREMENT_DISPOSITION_DEFERRED,
91
+ )
92
+
62
93
  REQUIREMENT_LEVEL_SOLUTION = 1
63
94
  REQUIREMENT_LEVEL_SEGMENT = 2
64
95
  REQUIREMENT_LEVEL_SERVICE = 3
@@ -12,7 +12,7 @@
12
12
  "name": "description",
13
13
  "min": 0,
14
14
  "max": 1,
15
- "description": "Free-form user-facing prose. Markdown allowed, multi-line via YAML '|' block scalar. Flows into doc-gen surfaces (JSDoc / XML-doc / Postgres COMMENT / Mermaid prose)."
15
+ "description": "What this element IS and COVERS, written for someone using it. Markdown allowed, multi-line via YAML '|' block scalar. Flows into doc-gen surfaces (JSDoc / XML-doc / Postgres COMMENT / Mermaid prose). State scope and boundary — what it covers, what it deliberately does NOT, and which sibling owns the rest — all of which is derivable from the model itself. Anything you had to read the implementation to learn belongs in @notes, not here."
16
16
  },
17
17
  {
18
18
  "type": "attr",
@@ -20,7 +20,7 @@
20
20
  "name": "summary",
21
21
  "min": 0,
22
22
  "max": 1,
23
- "description": "Short single-line tagline (OpenAPI `summary` pattern) — used in index tables, sidebar previews, and AI prompts where the full @description is too long. Optional supplement to @description; when @summary is unset, doc surfaces typically fall back to the first sentence of @description."
23
+ "description": "Short single-line SENTENCE (OpenAPI `summary` pattern) — used in index tables, sidebar previews, and AI prompts where the full @description is too long. Distinct from @title, which is a noun label rather than a sentence. When @summary is unset, doc surfaces typically fall back to the first sentence of @description."
24
24
  },
25
25
  {
26
26
  "type": "attr",
@@ -28,7 +28,7 @@
28
28
  "name": "title",
29
29
  "min": 0,
30
30
  "max": 1,
31
- "description": "Short single-line human label (e.g. 'Email' for a `field.string email`). Optional supplement to description."
31
+ "description": "Short single-line human label — a NOUN PHRASE naming the element (e.g. 'Email' for a `field.string email`), never a sentence. What a tab, an index row or a sidebar shows when the name is an identifier rather than a label. See @summary for the one-line sentence form."
32
32
  },
33
33
  {
34
34
  "type": "attr",
@@ -36,7 +36,7 @@
36
36
  "name": "notes",
37
37
  "min": 0,
38
38
  "max": 1,
39
- "description": "Internal-only rationale. Stays in metadata; never emitted to user-facing docs."
39
+ "description": "Internal-only rationale, never emitted to user-facing docs — the slot for what you had to look OUTSIDE the model to learn: evidence, measurements, citations, the control that proved an absence was real, and what breaks if this changes. It is NOT a longer @description, and restating the description here is the failure mode this slot invites. Mechanical test: a sentence belongs in @notes exactly when it would have to change because the IMPLEMENTATION changed while the model did not."
40
40
  },
41
41
  {
42
42
  "type": "attr",
@@ -0,0 +1,209 @@
1
+ {
2
+ "provider": "metaobjects-core-types",
3
+ "types": [
4
+ {
5
+ "type": "requirement",
6
+ "subType": "functional",
7
+ "description": "What the product does for a user, stated as one violable claim. Its check is EXISTENCE: it fails when nothing implements it. Hierarchy is nesting — an L1 solution contains its L2 segments, which contain L3 services, and so on down to the levels that reference the model.",
8
+ "whenToUse": "Something exists because someone asked for it. L1-L3 are levels of ABSTRACTION AND OWNERSHIP in the problem domain — whose need is this, and at what altitude — and are never a directory, package, deployable or module. Binding to technical constructs happens only at L4 (an object) and L5 (a member), which is the allocation step. Test every node: if a refactor that changes no behaviour would force it to move, its level is wrong.",
9
+ "children": [
10
+ {
11
+ "type": "attr",
12
+ "subType": "int",
13
+ "name": "level",
14
+ "min": 1,
15
+ "max": 1,
16
+ "description": "1 solution, 2 segment, 3 service, 4 object, 5 member. L1-L3 are levels of abstraction and ownership in the problem domain, NOT of code structure. Nesting depth must agree with it; skipping a level is legal, going back up is not."
17
+ },
18
+ {
19
+ "type": "attr",
20
+ "subType": "string",
21
+ "name": "status",
22
+ "min": 1,
23
+ "max": 1,
24
+ "allowedValues": [
25
+ "planned",
26
+ "live",
27
+ "partial",
28
+ "abandoned",
29
+ "superseded"
30
+ ],
31
+ "description": "planned intended but not built yet; live implemented and in use; partial implemented with known gaps; abandoned built then deliberately retired; superseded replaced by a different mechanism. A dangling @implementedBy is an ERROR on live/partial (the model moved, the requirement is stale) and ALLOWED on planned/abandoned/superseded — on planned the nodes do not exist YET, on the other two they are meant to be gone, and that is the entry doing its job. A planned requirement also never contributes to object coverage: planning a capability must not silence the warning that nothing implements it."
32
+ },
33
+ {
34
+ "type": "attr",
35
+ "subType": "string",
36
+ "name": "disposition",
37
+ "min": 0,
38
+ "max": 1,
39
+ "allowedValues": [
40
+ "accepted",
41
+ "deferred"
42
+ ],
43
+ "description": "What has been DECIDED about outstanding work — a different question from whether the work is done, which is what @status answers. accepted: the gap is understood and deliberately not being closed. deferred: it will be closed, but not now. ABSENT MEANS UNDECIDED, which is a real and useful state — it is what an unreviewed gap looks like, and it is the one a review should be able to find. Meaningful on planned and partial only; on a status with no outstanding work it is a WARNING. Deliberately NOT a workflow vocabulary: which sprint, who owns it and whether it is in progress belong in the tracker named by @trackedBy, because two systems holding that answer will drift and only one of them is refreshed daily."
44
+ },
45
+ {
46
+ "type": "attr",
47
+ "subType": "string",
48
+ "name": "trackedBy",
49
+ "isArray": true,
50
+ "min": 0,
51
+ "max": 1,
52
+ "description": "Issue or ticket references for outstanding work — a URL, an owner/repo#123 shorthand, or a tracker key. Free-form and NOT resolved by verify, which does not reach the network; unlike @verifiedBy, nothing here is checked to exist. Its job is to stop a deferred gap becoming invisible, so verify warns when a deferred requirement names no ticket. Also the right place to link the ticket that a planned requirement will be built under."
53
+ },
54
+ {
55
+ "type": "attr",
56
+ "subType": "string",
57
+ "name": "statement",
58
+ "min": 1,
59
+ "max": 1,
60
+ "description": "What the capability is, in one sentence."
61
+ },
62
+ {
63
+ "type": "attr",
64
+ "subType": "string",
65
+ "name": "violation",
66
+ "min": 1,
67
+ "max": 1,
68
+ "description": "What breaking it looks like, in one sentence. A requirement MUST be violable: 'every entity has a uuid primary key' is (point at one with a composite string key); 'things are persisted' is not, and is a description rather than a requirement."
69
+ },
70
+ {
71
+ "type": "attr",
72
+ "subType": "string",
73
+ "name": "implementedBy",
74
+ "isArray": true,
75
+ "min": 0,
76
+ "max": 1,
77
+ "description": "FQN references to the model nodes realising this requirement. Legal on level 4 (an object) and level 5 (a field, view or identity) only; an organisational level carrying it is ERR_REQUIREMENT_LINK_ABOVE_FLOOR. Many-to-many by construction — several requirements may name the same node."
78
+ },
79
+ {
80
+ "type": "attr",
81
+ "subType": "string",
82
+ "name": "verifiedBy",
83
+ "isArray": true,
84
+ "min": 0,
85
+ "max": 1,
86
+ "description": "Names of the tests proving the behaviour. verify checks each exists and is not skipped; it never runs them."
87
+ },
88
+ {
89
+ "type": "attr",
90
+ "subType": "string",
91
+ "name": "supersededBy",
92
+ "min": 0,
93
+ "max": 1,
94
+ "description": "The requirement that replaced this one. Expected on status=superseded."
95
+ },
96
+ {
97
+ "type": "requirement",
98
+ "subType": "*",
99
+ "name": "*",
100
+ "min": 0,
101
+ "max": null,
102
+ "description": "Nested child requirements. Hierarchy IS nesting — an L1 solution contains its L2 segments, which contain L3 services — so regrouping moves a node rather than editing a parent string, and a requirement is addressable by the same dotted child-name path as every other node."
103
+ }
104
+ ]
105
+ },
106
+ {
107
+ "type": "requirement",
108
+ "subType": "architectural",
109
+ "description": "How the system is built, applied uniformly across the model. Its check is UNIVERSALITY: it fails when something VIOLATES it, which is the opposite polarity to a functional requirement. Flat by default and object-independent; it may optionally sit in a levelled tree when a quality taxonomy is being used to organise non-functional requirements.",
110
+ "whenToUse": "Something exists because every entity here looks like this — a uuid primary key, an @autoSet createdAt, a change-attribution column, tenant scoping. The discriminator is mechanical: did this exist because someone asked for something, or because it is the architecture? For a non-functional tree, an established quality taxonomy makes a good fixed upper structure (e.g. an ISO/IEC 25010 characteristic at L1, its sub-characteristic or a control-catalogue category at L2), with the model-binding claims at L4 and L5 as usual.",
111
+ "children": [
112
+ {
113
+ "type": "attr",
114
+ "subType": "int",
115
+ "name": "level",
116
+ "min": 0,
117
+ "max": 1,
118
+ "description": "OPTIONAL here, unlike on a functional requirement where it is required. ABSENT means a flat, object-independent policy that may reference the model directly — the original and still the default form. PRESENT means this node sits in a levelled tree, and then the same rules as functional apply: nesting must agree with the level, and only L4/L5 may carry @implementedBy. Levelling is opt-in so that adding a taxonomy on top of existing flat policies does not invalidate them."
119
+ },
120
+ {
121
+ "type": "attr",
122
+ "subType": "string",
123
+ "name": "status",
124
+ "min": 1,
125
+ "max": 1,
126
+ "allowedValues": [
127
+ "planned",
128
+ "live",
129
+ "partial",
130
+ "abandoned",
131
+ "superseded"
132
+ ],
133
+ "description": "As on requirement.functional. A live or partial architectural requirement claimed by NOTHING is an error: a policy declared and applied to nothing. A planned one is exempt from that check — it is not applied yet by definition."
134
+ },
135
+ {
136
+ "type": "attr",
137
+ "subType": "string",
138
+ "name": "disposition",
139
+ "min": 0,
140
+ "max": 1,
141
+ "allowedValues": [
142
+ "accepted",
143
+ "deferred"
144
+ ],
145
+ "description": "As on requirement.functional. On an architectural requirement, accepted is the common and important case: a policy that is genuinely not universal, where the exceptions are known and tolerated, is more honest as partial+accepted than as a live claim nobody audits."
146
+ },
147
+ {
148
+ "type": "attr",
149
+ "subType": "string",
150
+ "name": "trackedBy",
151
+ "isArray": true,
152
+ "min": 0,
153
+ "max": 1,
154
+ "description": "As on requirement.functional. Issue or ticket references for outstanding work; free-form, not resolved."
155
+ },
156
+ {
157
+ "type": "attr",
158
+ "subType": "string",
159
+ "name": "statement",
160
+ "min": 1,
161
+ "max": 1,
162
+ "description": "The policy, in one sentence."
163
+ },
164
+ {
165
+ "type": "attr",
166
+ "subType": "string",
167
+ "name": "violation",
168
+ "min": 1,
169
+ "max": 1,
170
+ "description": "What breaking it looks like — the node that would contradict it. This is what makes universality checkable."
171
+ },
172
+ {
173
+ "type": "attr",
174
+ "subType": "string",
175
+ "name": "implementedBy",
176
+ "isArray": true,
177
+ "min": 0,
178
+ "max": 1,
179
+ "description": "FQN references to the nodes applying this policy. High fan-out is normal and expected: one uuid-primary-key requirement is claimed by every entity."
180
+ },
181
+ {
182
+ "type": "attr",
183
+ "subType": "string",
184
+ "name": "verifiedBy",
185
+ "isArray": true,
186
+ "min": 0,
187
+ "max": 1,
188
+ "description": "Names of the tests proving the policy holds. verify checks each exists and is not skipped; it never runs them."
189
+ },
190
+ {
191
+ "type": "attr",
192
+ "subType": "string",
193
+ "name": "supersededBy",
194
+ "min": 0,
195
+ "max": 1,
196
+ "description": "The requirement that replaced this one. Expected on status=superseded."
197
+ },
198
+ {
199
+ "type": "requirement",
200
+ "subType": "*",
201
+ "name": "*",
202
+ "min": 0,
203
+ "max": null,
204
+ "description": "Nested child requirements. Present so a non-functional tree can be organised by a quality taxonomy the same way a functional tree is organised by capability — the absence of this rule, while requirement.functional had it, is what previously made an architectural node nestable UNDER a functional one but never under another architectural one."
205
+ }
206
+ ]
207
+ }
208
+ ]
209
+ }
@@ -42,6 +42,9 @@ class PostgresContainer:
42
42
  """Context manager around ``docker run`` for a per-scenario Postgres."""
43
43
 
44
44
  IMAGE = "postgres:16-alpine"
45
+
46
+ #: Fresh-port retries before giving up -- see the port-race note in __init__.
47
+ START_ATTEMPTS = 3
45
48
  USER = "postgres"
46
49
  PASSWORD = "test"
47
50
  DATABASE = "postgres"
@@ -57,19 +60,45 @@ class PostgresContainer:
57
60
  self._shared = False
58
61
  self._admin_url = None
59
62
  self._name = f"metaobjects-test-{uuid.uuid4().hex[:8]}"
60
- self._port = _pick_free_port()
61
- self._info = PostgresInfo(
62
- host="localhost", port=self._port, database=self.DATABASE,
63
- user=self.USER, password=self.PASSWORD,
64
- )
65
- _docker(
66
- "run", "-d", "--rm",
67
- "--name", self._name,
68
- "-e", f"POSTGRES_PASSWORD={self.PASSWORD}",
69
- "-p", f"{self._port}:5432",
70
- self.IMAGE,
63
+ # PORT RACE. _pick_free_port() closes its probe socket and hands the number to
64
+ # docker, which binds it LATER -- a check-then-act gap. Alone it never bites;
65
+ # under a full CI run several ports start containers at once and two can be
66
+ # handed the same number. The loser's container is created and then dies, and a
67
+ # readiness probe that only asks `docker exec` cannot tell that from a slow
68
+ # boot. Retry on a FRESH port.
69
+ #
70
+ # Deliberately NOT `--rm`: an auto-removed container takes its logs with it, so
71
+ # the one artefact explaining the failure vanishes exactly when it is needed.
72
+ # close() force-removes, so nothing leaks.
73
+ last_failure: Exception | None = None
74
+ for _attempt in range(self.START_ATTEMPTS):
75
+ self._port = _pick_free_port()
76
+ self._info = PostgresInfo(
77
+ host="localhost", port=self._port, database=self.DATABASE,
78
+ user=self.USER, password=self.PASSWORD,
79
+ )
80
+ try:
81
+ _docker(
82
+ "run", "-d",
83
+ "--name", self._name,
84
+ "-e", f"POSTGRES_PASSWORD={self.PASSWORD}",
85
+ "-p", f"{self._port}:5432",
86
+ self.IMAGE,
87
+ )
88
+ except Exception as e: # noqa: BLE001 — docker refused the bind; retry on a new port
89
+ last_failure = e
90
+ self._force_remove()
91
+ continue
92
+ try:
93
+ self._wait_ready()
94
+ return
95
+ except Exception as e: # noqa: BLE001 — died on startup; retry on a new port
96
+ last_failure = e
97
+ self._force_remove()
98
+ raise RuntimeError(
99
+ f"postgres container '{self._name}' failed to start in "
100
+ f"{self.START_ATTEMPTS} attempts; last err: {last_failure}"
71
101
  )
72
- self._wait_ready()
73
102
 
74
103
  # -- Shared-sidecar mode -------------------------------------------------
75
104
  # CREATE a fresh, uniquely-named database on the already-running Postgres
@@ -138,11 +167,50 @@ class PostgresContainer:
138
167
  # finishes; mirrors the TS/Java two-phase wait).
139
168
  # -----------------------------------------------------------------------
140
169
 
170
+ # READINESS WINDOW. 30s was too tight: under a FULL local-CI run several ports
171
+ # spin their own Postgres concurrently, and a container that is merely slow to
172
+ # boot produces a red gate indistinguishable from a real failure -- it fires
173
+ # BEFORE any test logic. Raising the bound costs nothing when the container is
174
+ # ready quickly (the loop polls every 250ms and returns immediately) and only
175
+ # changes how long the pathological case takes to give up.
176
+ def _force_remove(self) -> None:
177
+ """Best-effort teardown between start attempts."""
178
+ try:
179
+ _docker("rm", "-f", self._name)
180
+ except Exception: # noqa: BLE001 — already gone
181
+ pass
182
+
183
+ def _inspect_state(self) -> str:
184
+ """The container's docker state, or 'gone' when it cannot be determined."""
185
+ r = subprocess.run(
186
+ ["docker", "inspect", "-f", "{{.State.Status}}", self._name],
187
+ capture_output=True, text=True,
188
+ )
189
+ return r.stdout.strip() if r.returncode == 0 else "gone"
190
+
191
+ def _tail_logs(self) -> str:
192
+ """Last few log lines, for an error that would otherwise name only a symptom."""
193
+ r = subprocess.run(
194
+ ["docker", "logs", "--tail", "40", self._name], capture_output=True, text=True,
195
+ )
196
+ return (r.stdout + r.stderr).strip() if r.returncode == 0 else "(docker logs unavailable)"
197
+
141
198
  def _wait_ready(self) -> None:
142
199
  import pg8000.dbapi # local import — pg8000 lives in the `integration` extra
143
- deadline = time.monotonic() + 30
200
+ timeout_s = int(os.environ.get("MO_PG_READY_TIMEOUT_S", "120"))
201
+ deadline = time.monotonic() + timeout_s
144
202
  last_err: Exception | None = None
145
203
  while time.monotonic() < deadline:
204
+ # FAIL FAST ON A DEAD CONTAINER. `docker exec` alone cannot tell "still
205
+ # booting" from "exited seconds ago" -- both just fail -- so a container that
206
+ # died on startup used to burn the whole window and report a TIMEOUT, which
207
+ # reads as slowness and is not. Ask docker for its state and surface its logs.
208
+ state = self._inspect_state()
209
+ if state != "running":
210
+ raise RuntimeError(
211
+ f"postgres container '{self._name}' is '{state}', not running -- it died "
212
+ f"during startup rather than being slow. docker logs:\n{self._tail_logs()}"
213
+ )
146
214
  ready = subprocess.run(
147
215
  ["docker", "exec", self._name, "pg_isready", "-U", self.USER],
148
216
  capture_output=True,
@@ -159,7 +227,9 @@ class PostgresContainer:
159
227
  last_err = e
160
228
  time.sleep(0.25)
161
229
  raise RuntimeError(
162
- f"postgres container '{self._name}' did not become ready within 30s; last err: {last_err}"
230
+ f"postgres container '{self._name}' did not become ready within {timeout_s}s "
231
+ f"(state={self._inspect_state()}); last err: {last_err}. "
232
+ f"docker logs:\n{self._tail_logs()}"
163
233
  )
164
234
 
165
235