matrx-orm 3.1.9__tar.gz → 3.1.11__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 (276) hide show
  1. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/PKG-INFO +1 -1
  2. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/__init__.py +10 -0
  3. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/async_db_manager.py +15 -1
  4. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/config.py +18 -0
  5. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/query/executor.py +6 -0
  6. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/__init__.py +14 -1
  7. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/errors.py +28 -0
  8. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/managed.py +256 -18
  9. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/session.py +28 -0
  10. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/pyproject.toml +1 -1
  11. matrx_orm-3.1.11/tests/level1/test_managed_write_guard.py +226 -0
  12. matrx_orm-3.1.9/tests/level1/test_managed_write_guard.py +0 -104
  13. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/.gitignore +0 -0
  14. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/BASE_CLASS_METHODS.md +0 -0
  15. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/CLAUDE.md +0 -0
  16. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/MODEL_API.md +0 -0
  17. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/README.md +0 -0
  18. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/database/orm/extended/managers/ai_model_base.py +0 -0
  19. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/docs/migrations.md +0 -0
  20. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/MODULE_README.md +0 -0
  21. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/adapters/MODULE_README.md +0 -0
  22. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/adapters/__init__.py +0 -0
  23. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/adapters/async_postgresql.py +0 -0
  24. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/adapters/base_adapter.py +0 -0
  25. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/adapters/postgresql.py +0 -0
  26. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  27. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/adapters/supabase_adapter.py +0 -0
  28. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/admin/__init__.py +0 -0
  29. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/admin/router.py +0 -0
  30. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/api/__init__.py +0 -0
  31. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/api/auth.py +0 -0
  32. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/api/config.py +0 -0
  33. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/api/handlers.py +0 -0
  34. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/api/protocol.py +0 -0
  35. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/api/server.py +0 -0
  36. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/associations.py +0 -0
  37. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/cache_debug.py +0 -0
  38. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/client/MODULE_README.md +0 -0
  39. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/client/__init__.py +0 -0
  40. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/client/postgres_connection.py +0 -0
  41. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/client/postgrest.py +0 -0
  42. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/client/supabase_auth.py +0 -0
  43. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/client/supabase_config.py +0 -0
  44. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/client/supabase_manager.py +0 -0
  45. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  46. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/MODULE_README.md +0 -0
  47. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  48. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/TASKS.md +0 -0
  49. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/__init__.py +0 -0
  50. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/base.py +0 -0
  51. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/db_function.py +0 -0
  52. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/diagnostics.py +0 -0
  53. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/expressions.py +0 -0
  54. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/extended.py +0 -0
  55. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/fields.py +0 -0
  56. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/introspection.py +0 -0
  57. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/listen.py +0 -0
  58. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/loop_filters.py +0 -0
  59. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/model_dto.py +0 -0
  60. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/model_view.py +0 -0
  61. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/paginator.py +0 -0
  62. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/pydantic_bridge.py +0 -0
  63. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/registry.py +0 -0
  64. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/relations.py +0 -0
  65. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/resilience.py +0 -0
  66. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/rls_session.py +0 -0
  67. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/signals.py +0 -0
  68. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/transaction.py +0 -0
  69. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/types.py +0 -0
  70. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/core/write_queue.py +0 -0
  71. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/entity.py +0 -0
  72. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/error_handling.py +0 -0
  73. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/exceptions.py +0 -0
  74. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/extended/__init__.py +0 -0
  75. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/extended/app_error_handler.py +0 -0
  76. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/middleware/__init__.py +0 -0
  77. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/middleware/base.py +0 -0
  78. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/MODULE_README.md +0 -0
  79. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/__init__.py +0 -0
  80. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/cli.py +0 -0
  81. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/ddl.py +0 -0
  82. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/diff.py +0 -0
  83. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/executor.py +0 -0
  84. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/integration.py +0 -0
  85. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/loader.py +0 -0
  86. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/operations.py +0 -0
  87. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/state.py +0 -0
  88. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/migrations/table_filter.py +0 -0
  89. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/MODULE_README.md +0 -0
  90. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/__init__.py +0 -0
  91. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/bulk_update_values.py +0 -0
  92. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/conflict.py +0 -0
  93. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/conflict_writes.py +0 -0
  94. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/create.py +0 -0
  95. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/delete.py +0 -0
  96. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/dynamic_admin.py +0 -0
  97. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/queue_claim.py +0 -0
  98. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/read.py +0 -0
  99. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/staging_load.py +0 -0
  100. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/operations/update.py +0 -0
  101. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/py.typed +0 -0
  102. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/python_sql/MODULE_README.md +0 -0
  103. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/python_sql/__init__.py +0 -0
  104. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/python_sql/db_objects.py +0 -0
  105. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  106. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  107. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/query/MODULE_README.md +0 -0
  108. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/query/__init__.py +0 -0
  109. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/query/builder.py +0 -0
  110. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  111. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/__init__.py +0 -0
  112. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/code_handler.py +0 -0
  113. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  114. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/columns.py +0 -0
  115. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/common.py +0 -0
  116. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/diff_preview.py +0 -0
  117. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  118. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/generator.py +0 -0
  119. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  120. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  121. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  122. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  123. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/package_wiring.py +0 -0
  124. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/relationships.py +0 -0
  125. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/runner.py +0 -0
  126. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/schema.py +0 -0
  127. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/schema_manager.py +0 -0
  128. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/tables.py +0 -0
  129. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/schema_builder/views.py +0 -0
  130. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/README.md +0 -0
  131. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/coalesce.py +0 -0
  132. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/dag.py +0 -0
  133. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/fallback.py +0 -0
  134. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/flush.py +0 -0
  135. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/lifecycle.py +0 -0
  136. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/op.py +0 -0
  137. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/reads.py +0 -0
  138. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/session/telemetry.py +0 -0
  139. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  140. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/sql_executor/__init__.py +0 -0
  141. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/sql_executor/executor.py +0 -0
  142. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/sql_executor/queries.py +0 -0
  143. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/sql_executor/registry.py +0 -0
  144. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/sql_executor/types.py +0 -0
  145. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/sql_executor/utils.py +0 -0
  146. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/state.py +0 -0
  147. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/utils/__init__.py +0 -0
  148. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/utils/sql_utils.py +0 -0
  149. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/matrx_orm/utils/type_converters.py +0 -0
  150. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/scripts/git-branches.sh +0 -0
  151. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/scripts/publish.sh +0 -0
  152. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/scripts/release.sh +0 -0
  153. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/MODULE_README.md +0 -0
  154. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/__init__.py +0 -0
  155. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/conftest.py +0 -0
  156. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/MODULE_README.md +0 -0
  157. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/__init__.py +0 -0
  158. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_admin_db_columns.py +0 -0
  159. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_admin_search_sentinels.py +0 -0
  160. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_advisory_lock.py +0 -0
  161. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_agent_message.py +0 -0
  162. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_api_auth.py +0 -0
  163. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_api_config.py +0 -0
  164. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_api_handlers.py +0 -0
  165. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_api_protocol.py +0 -0
  166. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_array_agg_order_by.py +0 -0
  167. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_associations.py +0 -0
  168. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_bulk_update_by_pk.py +0 -0
  169. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  170. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_cache_debug.py +0 -0
  171. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_call_function.py +0 -0
  172. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_case_when.py +0 -0
  173. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_composite_pk_filter.py +0 -0
  174. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_composite_pk_write_paths.py +0 -0
  175. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_config.py +0 -0
  176. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_conflict_target.py +0 -0
  177. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_conflict_writes.py +0 -0
  178. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_connection_codecs.py +0 -0
  179. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_connection_poison_guard.py +0 -0
  180. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_count_composite_distinct.py +0 -0
  181. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_db_function_field.py +0 -0
  182. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_ddl_generator.py +0 -0
  183. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_dirty_tracking.py +0 -0
  184. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_exceptions.py +0 -0
  185. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_execute_admin_sql.py +0 -0
  186. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_expression_primitives.py +0 -0
  187. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_fields.py +0 -0
  188. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  189. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_filter_raw.py +0 -0
  190. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_from_alias.py +0 -0
  191. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_fts.py +0 -0
  192. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_func_expression_args.py +0 -0
  193. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_having_annotate_alias.py +0 -0
  194. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  195. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_introspect_rls_policies.py +0 -0
  196. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_join_and_aggregate.py +0 -0
  197. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_listen_notify.py +0 -0
  198. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_matrx_entity.py +0 -0
  199. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_migration_diff_types.py +0 -0
  200. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_migration_loader.py +0 -0
  201. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_model_instance.py +0 -0
  202. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_model_meta.py +0 -0
  203. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_order_by_expression.py +0 -0
  204. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_patch_jsonb_path.py +0 -0
  205. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_postgrest_filters.py +0 -0
  206. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_query_builder.py +0 -0
  207. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_query_error_detail.py +0 -0
  208. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_query_executor_sql.py +0 -0
  209. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_query_timeout.py +0 -0
  210. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  211. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_queue_claim.py +0 -0
  212. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_registry.py +0 -0
  213. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_registry_multi_database.py +0 -0
  214. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_registry_multi_schema.py +0 -0
  215. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_relations.py +0 -0
  216. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_resilience.py +0 -0
  217. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_schema_exists.py +0 -0
  218. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_session_advisory_lock.py +0 -0
  219. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_state_cache.py +0 -0
  220. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_subquery_filter_raw.py +0 -0
  221. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_subquery_in_filter.py +0 -0
  222. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_supabase_auth.py +0 -0
  223. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_supabase_config.py +0 -0
  224. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_update_case_expression.py +0 -0
  225. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_update_subquery.py +0 -0
  226. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_upsert_with_conflict.py +0 -0
  227. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_values_jsonb_decode.py +0 -0
  228. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_watched_lifecycle.py +0 -0
  229. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level1/test_write_retry_semantics.py +0 -0
  230. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/MODULE_README.md +0 -0
  231. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/__init__.py +0 -0
  232. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/conftest.py +0 -0
  233. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_bulk_ops.py +0 -0
  234. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_cache_integration.py +0 -0
  235. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_crud.py +0 -0
  236. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_foreign_keys.py +0 -0
  237. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_m2m.py +0 -0
  238. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_manager.py +0 -0
  239. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_migrations_live.py +0 -0
  240. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_query_execution.py +0 -0
  241. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/level2/test_schema_diff.py +0 -0
  242. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project/.env.example +0 -0
  243. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project/README.md +0 -0
  244. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project/__init__.py +0 -0
  245. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project/generate.py +0 -0
  246. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project/generated/.gitkeep +0 -0
  247. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project/matrx_orm.yaml +0 -0
  248. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project/test_schema_generation.py +0 -0
  249. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project_desktop/.env.example +0 -0
  250. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project_desktop/README.md +0 -0
  251. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project_desktop/__init__.py +0 -0
  252. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project_desktop/client_example.py +0 -0
  253. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project_desktop/client_example.ts +0 -0
  254. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  255. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/sample_project_desktop/server.py +0 -0
  256. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/schema/entity_tests.py +0 -0
  257. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/schema/test_base_generation.py +0 -0
  258. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  259. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/schema/test_generate_schema.py +0 -0
  260. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/schema/test_multi_schema_output.py +0 -0
  261. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/__init__.py +0 -0
  262. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/conftest.py +0 -0
  263. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_capture_poison_proof.py +0 -0
  264. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_coalesce.py +0 -0
  265. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_dag.py +0 -0
  266. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_disk_spill_fallback.py +0 -0
  267. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_flush_individual_fallback.py +0 -0
  268. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_governed_write.py +0 -0
  269. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_managed.py +0 -0
  270. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_op_enqueue_site.py +0 -0
  271. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_reads.py +0 -0
  272. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/session/test_session.py +0 -0
  273. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/test_database_name_alias.py +0 -0
  274. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/test_model_cls_refactor.py +0 -0
  275. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/test_sql_param_casts.py +0 -0
  276. {matrx_orm-3.1.9 → matrx_orm-3.1.11}/tests/test_sync_twins_guard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrx-orm
3
- Version: 3.1.9
3
+ Version: 3.1.11
4
4
  Summary: Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching
5
5
  Project-URL: Homepage, https://github.com/AI-Matrix-Engine/aidream-current
6
6
  Project-URL: Repository, https://github.com/AI-Matrix-Engine/aidream-current
@@ -270,6 +270,7 @@ def _lazy_api():
270
270
  # ordering, coalescing, pending-aware reads, managed-write enforcement,
271
271
  # transactional flush, watchdog integration. See matrx_orm/session/.
272
272
  from matrx_orm.session import (
273
+ CoordinatorWriteViolation,
273
274
  DependencyCycleError,
274
275
  FlushError,
275
276
  FlushReport,
@@ -281,12 +282,16 @@ from matrx_orm.session import (
281
282
  SessionPhase,
282
283
  WatchedLifecycleConfig,
283
284
  WatchedLifecycleRegistry,
285
+ COORDINATOR_BYPASS_ACKNOWLEDGEMENT,
286
+ allow_direct_coordinator_write,
287
+ coordinator_policy_tables,
284
288
  current_session,
285
289
  declare_managed,
286
290
  drain_spilled_ops,
287
291
  is_in_session,
288
292
  record_error,
289
293
  record_failures,
294
+ register_coordinator_owned_model,
290
295
  )
291
296
  from matrx_orm.session import (
292
297
  run_sweeper as run_lifecycle_sweeper,
@@ -296,6 +301,11 @@ from matrx_orm.session import (
296
301
  )
297
302
 
298
303
  __all__ = [
304
+ "CoordinatorWriteViolation",
305
+ "COORDINATOR_BYPASS_ACKNOWLEDGEMENT",
306
+ "allow_direct_coordinator_write",
307
+ "coordinator_policy_tables",
308
+ "register_coordinator_owned_model",
299
309
  # ── Configuration ──────────────────────────────────────────────────────────
300
310
  "DatabaseProjectConfig",
301
311
  "register_database",
@@ -36,6 +36,20 @@ from matrx_orm.exceptions import (
36
36
 
37
37
  logger = logging.getLogger("matrx_orm.async_db_manager")
38
38
 
39
+
40
+ def _resolve_ssl(value: Any) -> Any:
41
+ """Normalize DatabaseProjectConfig.ssl for asyncpg's ``ssl=`` kwarg.
42
+
43
+ ``False`` / ``"disable"`` / ``"off"`` → no TLS (Coolify / local Postgres).
44
+ Everything else is passed through (default ``"require"`` for Supabase).
45
+ """
46
+ if value is False or value is None:
47
+ return False
48
+ if isinstance(value, str) and value.strip().lower() in {"disable", "off", "false", "0"}:
49
+ return False
50
+ return value
51
+
52
+
39
53
  # asyncpg SQLSTATE class 42 "undefined …" — the SQL named something the live DB
40
54
  # does not have. All siblings of SyntaxOrAccessError; PostgresSyntaxError is
41
55
  # caught separately (it's an ORM-bug shape, not a schema-drift shape). These map
@@ -409,7 +423,7 @@ class AsyncDatabaseManager:
409
423
  max_size=config.get("pool_max", 30),
410
424
  command_timeout=config.get("command_timeout", 10),
411
425
  max_inactive_connection_lifetime=max_inactive_connection_lifetime,
412
- ssl="require",
426
+ ssl=_resolve_ssl(config.get("ssl", "require")),
413
427
  statement_cache_size=statement_cache_size,
414
428
  server_settings=server_settings,
415
429
  init=init_callback,
@@ -83,6 +83,14 @@ class DatabaseProjectConfig:
83
83
  # Supabase compute can spike first-query latency on cold connections).
84
84
  server_settings: dict[str, str] = field(default_factory=dict)
85
85
 
86
+ # TLS for the asyncpg pool. Default ``"require"`` matches Supabase /
87
+ # managed Postgres (which reject plaintext). Direct Coolify / local
88
+ # Postgres images typically have ``ssl=off`` — set ``ssl=False`` (or
89
+ # ``"disable"``) for those. Accepted values are whatever asyncpg accepts
90
+ # for its ``ssl=`` kwarg (``False``, ``True``, ``"require"``, an
91
+ # ``ssl.SSLContext``, …).
92
+ ssl: Any = "require"
93
+
86
94
  # Write queue — gates write operations behind an async queue so they wait in
87
95
  # order instead of racing for pool connections and timing out under load.
88
96
  write_queue_enabled: bool = True
@@ -242,6 +250,7 @@ class DatabaseRegistry:
242
250
  "command_timeout": config.command_timeout,
243
251
  "session_init_sql": list(config.session_init_sql),
244
252
  "server_settings": dict(config.server_settings),
253
+ "ssl": config.ssl,
245
254
  "write_queue_enabled": config.write_queue_enabled,
246
255
  "write_concurrency": config.write_concurrency,
247
256
  "write_queue_size": config.write_queue_size,
@@ -408,6 +417,7 @@ def register_database_from_env(
408
417
  command_timeout: int | None = None,
409
418
  session_init_sql: list[str] | None = None,
410
419
  server_settings: dict[str, str] | None = None,
420
+ ssl: Any = None,
411
421
  write_queue_enabled: bool | None = None,
412
422
  write_concurrency: int | None = None,
413
423
  write_queue_size: int | None = None,
@@ -562,6 +572,13 @@ def register_database_from_env(
562
572
  supabase_secret_key or os.environ.get(_supabase_secret_env, "").strip()
563
573
  )
564
574
 
575
+ _ssl_env = _overrides.get("SSL", f"{env_prefix}_SSL")
576
+ if ssl is not None:
577
+ _resolved_ssl: Any = ssl
578
+ else:
579
+ _ssl_raw = os.environ.get(_ssl_env, "").strip()
580
+ _resolved_ssl = _ssl_raw if _ssl_raw else "require"
581
+
565
582
  try:
566
583
  config = DatabaseProjectConfig(
567
584
  name=name,
@@ -582,6 +599,7 @@ def register_database_from_env(
582
599
  supabase_secret_key=_resolved_supabase_secret_key,
583
600
  session_init_sql=list(session_init_sql or []),
584
601
  server_settings=dict(server_settings or {}),
602
+ ssl=_resolved_ssl,
585
603
  **pool_kwargs,
586
604
  )
587
605
  registry.register(config)
@@ -957,6 +957,12 @@ class QueryExecutor:
957
957
  (default ``BaseAdapter`` behaviour) so callers can fall through to the
958
958
  SQL path.
959
959
  """
960
+ from matrx_orm.session.managed import assert_model_write_allowed
961
+
962
+ assert_model_write_allowed(
963
+ self.model,
964
+ str(query.get("operation") or "adapter write"),
965
+ )
960
966
  return await self.db.execute_write_dict(query)
961
967
 
962
968
  async def insert(self, query: dict[str, Any]) -> dict[str, Any]:
@@ -50,6 +50,7 @@ Usage:
50
50
  from matrx_orm.session.coalesce import coalesce_ops, summarize
51
51
  from matrx_orm.session.dag import DependencyCycleError, tier_ops, tier_summary
52
52
  from matrx_orm.session.errors import (
53
+ CoordinatorWriteViolation,
53
54
  FlushError,
54
55
  ManagedWriteViolation,
55
56
  SessionClosedError,
@@ -65,7 +66,14 @@ from matrx_orm.session.lifecycle import (
65
66
  run_sweeper,
66
67
  sweep_once,
67
68
  )
68
- from matrx_orm.session.managed import declare_managed, install_managed_writes
69
+ from matrx_orm.session.managed import (
70
+ COORDINATOR_BYPASS_ACKNOWLEDGEMENT,
71
+ allow_direct_coordinator_write,
72
+ coordinator_policy_tables,
73
+ declare_managed,
74
+ install_managed_writes,
75
+ register_coordinator_owned_model,
76
+ )
69
77
  from matrx_orm.session.op import (
70
78
  OpType,
71
79
  SessionOp,
@@ -90,6 +98,7 @@ from matrx_orm.session.session import (
90
98
 
91
99
  __all__ = [
92
100
  "DependencyCycleError",
101
+ "CoordinatorWriteViolation",
93
102
  "FlushError",
94
103
  "FlushReport",
95
104
  "ManagedWriteViolation",
@@ -106,9 +115,13 @@ __all__ = [
106
115
  "coalesce_ops",
107
116
  "config_from_meta",
108
117
  "current_session",
118
+ "COORDINATOR_BYPASS_ACKNOWLEDGEMENT",
119
+ "allow_direct_coordinator_write",
120
+ "coordinator_policy_tables",
109
121
  "declare_managed",
110
122
  "has_pending_in_stack",
111
123
  "install_managed_writes",
124
+ "register_coordinator_owned_model",
112
125
  "is_in_session",
113
126
  "pending_ops_across_stack",
114
127
  "make_delete",
@@ -48,6 +48,34 @@ class ManagedWriteViolation(SessionError):
48
48
  self.op_kind = op_kind
49
49
 
50
50
 
51
+ class CoordinatorWriteViolation(ManagedWriteViolation):
52
+ """Raised when a coordinator-owned Model is written by another path."""
53
+
54
+ def __init__(
55
+ self,
56
+ model_name: str,
57
+ op_kind: str,
58
+ *,
59
+ required_scope: str,
60
+ actual_scope: str | None,
61
+ ) -> None:
62
+ SessionError.__init__(
63
+ self,
64
+ f"{model_name} is coordinator-owned (required write scope: "
65
+ f"{required_scope!r}). Direct {op_kind} is forbidden; the active "
66
+ f"Session scope is {actual_scope!r}. Queue this write through its "
67
+ "Coordinator so parent and child rows share one transactional DAG. "
68
+ "For a genuine out-of-request maintenance operation, use "
69
+ "allow_direct_coordinator_write(model, reason='...', "
70
+ "acknowledgement="
71
+ "'YES_I_KNOW_I_AM_SKIPPING_THE_COORDINATOR_ON_PURPOSE') explicitly.",
72
+ )
73
+ self.model_name = model_name
74
+ self.op_kind = op_kind
75
+ self.required_scope = required_scope
76
+ self.actual_scope = actual_scope
77
+
78
+
51
79
  class SessionClosedError(SessionError):
52
80
  """Raised when an op is queued onto a Session that has already flushed
53
81
  or errored. Generally callers should use ``async with Session():`` so
@@ -1,8 +1,8 @@
1
1
  """Managed Model write interception — the runtime enforcement layer.
2
2
 
3
3
  A Model opts into Session-aware writes by declaring
4
- ``Meta.managed_writes`` as either ``"elevate"`` (default behavior when
5
- ``True``) or ``"strict"``::
4
+ ``Meta.managed_writes`` as ``"elevate"`` (default behavior when ``True``),
5
+ ``"strict"``, or ``"coordinator"``::
6
6
 
7
7
  class Conversation(Model):
8
8
  ...
@@ -10,6 +10,8 @@ A Model opts into Session-aware writes by declaring
10
10
  managed_writes = "elevate" # auto-open one-shot if no Session
11
11
  # OR
12
12
  managed_writes = "strict" # raise ManagedWriteViolation if no Session
13
+ # OR
14
+ managed_writes = "coordinator" # audit writes lacking owner authority
13
15
 
14
16
  When the Model's ``create`` / ``update`` / ``delete`` / ``bulk_*`` /
15
17
  ``upsert`` methods are called:
@@ -22,6 +24,9 @@ When the Model's ``create`` / ``update`` / ``delete`` / ``bulk_*`` /
22
24
  was a direct write.
23
25
  3. If NO active Session AND mode == ``"strict"``, raise
24
26
  :class:`ManagedWriteViolation` — loud red banner, full stack trace.
27
+ 4. Coordinator mode never blocks a write. A write without the Coordinator's
28
+ opaque authority (or an acknowledged exact-model override) continues and
29
+ emits a large red ERROR diagnostic. Authorized writes are silent.
25
30
 
26
31
  For backward compatibility, this layer activates only when
27
32
  ``managed_writes`` is truthy. Unmanaged Models keep their current
@@ -49,12 +54,18 @@ from __future__ import annotations
49
54
  import functools
50
55
  import logging
51
56
  import re
52
- from contextlib import asynccontextmanager
57
+ from contextlib import asynccontextmanager, contextmanager
58
+ from contextvars import ContextVar
59
+ from dataclasses import dataclass
60
+ from collections.abc import Iterator
53
61
  from typing import Any
54
62
 
55
63
  from matrx_utils import vcprint
56
64
 
57
- from matrx_orm.session.errors import MANAGED_WRITE_SKILL_HINT, ManagedWriteViolation
65
+ from matrx_orm.session.errors import (
66
+ MANAGED_WRITE_SKILL_HINT,
67
+ ManagedWriteViolation,
68
+ )
58
69
  from matrx_orm.session.session import Session, SessionPhase, session_stack
59
70
 
60
71
  logger = logging.getLogger("matrx_orm.session.managed")
@@ -82,6 +93,71 @@ logger = logging.getLogger("matrx_orm.session.managed")
82
93
 
83
94
  _STRICT_TABLES: set[str] = set()
84
95
 
96
+ # table token -> required Session.write_scope. This is stronger than strict:
97
+ # merely opening a Session/transaction is not authority to write the table.
98
+ _COORDINATOR_TABLES: dict[str, str] = {}
99
+
100
+ _COORDINATOR_AUTHORITY = object()
101
+
102
+ COORDINATOR_BYPASS_ACKNOWLEDGEMENT = (
103
+ "YES_I_KNOW_I_AM_SKIPPING_THE_COORDINATOR_ON_PURPOSE"
104
+ )
105
+
106
+
107
+ @dataclass(frozen=True, slots=True)
108
+ class DirectWriteException:
109
+ model_cls: type
110
+ reason: str
111
+
112
+
113
+ _direct_write_exceptions: ContextVar[tuple[DirectWriteException, ...]] = ContextVar(
114
+ "matrx_orm_direct_write_exceptions",
115
+ default=(),
116
+ )
117
+
118
+
119
+ @contextmanager
120
+ def allow_direct_coordinator_write(
121
+ model_cls: type,
122
+ *,
123
+ reason: str,
124
+ acknowledgement: str,
125
+ ) -> Iterator[None]:
126
+ """Temporarily acknowledge one model's intentional Coordinator bypass."""
127
+ cleaned = reason.strip()
128
+ if not cleaned:
129
+ raise ValueError("allow_direct_coordinator_write requires a non-empty reason")
130
+ if acknowledgement != COORDINATOR_BYPASS_ACKNOWLEDGEMENT:
131
+ raise ValueError(
132
+ "allow_direct_coordinator_write requires acknowledgement="
133
+ f"{COORDINATOR_BYPASS_ACKNOWLEDGEMENT!r}"
134
+ )
135
+ exception = DirectWriteException(model_cls, cleaned)
136
+ token = _direct_write_exceptions.set(_direct_write_exceptions.get() + (exception,))
137
+ try:
138
+ yield
139
+ finally:
140
+ _direct_write_exceptions.reset(token)
141
+
142
+
143
+ def _has_direct_exception(model_cls: type) -> bool:
144
+ return any(item.model_cls is model_cls for item in _direct_write_exceptions.get())
145
+
146
+
147
+ def _coordinator_session(*, database: str | None = None) -> Session:
148
+ """Create the Session capability reserved for matrx-ai's Coordinator."""
149
+ from matrx_orm.session.session import Session as SessionClass
150
+
151
+ return SessionClass(
152
+ database=database,
153
+ write_scope="coordinator",
154
+ _write_authority=_COORDINATOR_AUTHORITY,
155
+ )
156
+
157
+
158
+ def _has_coordinator_authority(session: Session | None) -> bool:
159
+ return session is not None and session._has_write_authority(_COORDINATOR_AUTHORITY)
160
+
85
161
  # Leading INSERT INTO / UPDATE / DELETE FROM target — ORM-generated SQL only.
86
162
  _WRITE_TARGET_RE = re.compile(
87
163
  r"^\s*(?:INSERT\s+INTO|UPDATE|DELETE\s+FROM)\s+([^\s(]+)",
@@ -101,6 +177,71 @@ def _register_strict_table(model_cls: type) -> None:
101
177
  _STRICT_TABLES.add(str(name).strip('"'))
102
178
 
103
179
 
180
+ def _register_coordinator_table(model_cls: type, required_scope: str) -> None:
181
+ meta = getattr(model_cls, "_meta", None)
182
+ if meta is None:
183
+ return
184
+ for name in (
185
+ getattr(meta, "qualified_table_name", None),
186
+ getattr(meta, "table_name", None),
187
+ ):
188
+ if name:
189
+ normalized = str(name).strip('"')
190
+ _COORDINATOR_TABLES[normalized] = required_scope
191
+ _STRICT_TABLES.discard(normalized)
192
+
193
+
194
+ def register_coordinator_owned_model(entry: Any) -> None:
195
+ """Host registrar consumed by matrx-ai's package-independent registry."""
196
+ model_cls = entry.model_cls
197
+ declare_managed(model_cls, mode="coordinator", write_scope="coordinator")
198
+
199
+
200
+ def coordinator_policy_tables() -> dict[str, str]:
201
+ """Return installed table policies for startup/release diagnostics."""
202
+ return dict(_COORDINATOR_TABLES)
203
+
204
+
205
+ def _coordinator_scope_for_model(model_cls: type) -> str | None:
206
+ meta = getattr(model_cls, "_meta", None)
207
+ if meta is None:
208
+ return None
209
+ for name in (
210
+ getattr(meta, "qualified_table_name", None),
211
+ getattr(meta, "table_name", None),
212
+ ):
213
+ if name and (scope := _COORDINATOR_TABLES.get(str(name).strip('"'))):
214
+ return scope
215
+ return None
216
+
217
+
218
+ def assert_model_write_allowed(
219
+ model_cls: type,
220
+ op_kind: str,
221
+ *,
222
+ session: Session | None = None,
223
+ ) -> bool:
224
+ """Warn on a Coordinator-owned Model write without Coordinator authority."""
225
+ required_scope = _coordinator_scope_for_model(model_cls)
226
+ if required_scope is None or _has_direct_exception(model_cls):
227
+ return True
228
+ active = session
229
+ if active is None:
230
+ for candidate in reversed(session_stack()):
231
+ if candidate.phase is SessionPhase.OPEN:
232
+ active = candidate
233
+ break
234
+ actual_scope = active.write_scope if active is not None else None
235
+ if _has_coordinator_authority(active):
236
+ return True
237
+ banner = _format_coordinator_banner(
238
+ model_cls.__name__, op_kind, required_scope, actual_scope
239
+ )
240
+ vcprint(banner, color="red")
241
+ logger.error("CoordinatorWriteViolation: %s", banner)
242
+ return False
243
+
244
+
104
245
  def _has_active_open_session() -> bool:
105
246
  for candidate in reversed(session_stack()):
106
247
  if candidate.phase is SessionPhase.OPEN:
@@ -116,12 +257,37 @@ def assert_managed_write_allowed(query: str) -> None:
116
257
  — when no strict table exists. Best-effort table parsing: ORM-generated DML
117
258
  only; a query we can't parse is allowed through (the wrappers + the strict
118
259
  create path remain the first line of defense)."""
119
- if not _STRICT_TABLES:
260
+ if not _STRICT_TABLES and not _COORDINATOR_TABLES:
120
261
  return
121
- # Governed contexts always allowed:
262
+ # Session.defer_* is authority-checked before an op can enter a flush.
263
+ # The flush may run in a deliberately detached context where its Session
264
+ # stack is absent, so the internal execution phase is trusted here.
122
265
  from matrx_orm.session.flush import is_flushing
123
266
 
124
- if is_flushing() or _has_active_open_session():
267
+ if is_flushing():
268
+ return
269
+ m = _WRITE_TARGET_RE.match(query)
270
+ if not m:
271
+ return
272
+ token = m.group(1).strip().strip('"')
273
+ bare_token = token.split(".")[-1].strip('"')
274
+
275
+ required_scope = _COORDINATOR_TABLES.get(token) or _COORDINATOR_TABLES.get(bare_token)
276
+ if required_scope is not None:
277
+ model_cls = _model_for_coordinator_table(token, bare_token)
278
+ if model_cls is not None:
279
+ assert_model_write_allowed(model_cls, "write")
280
+ else:
281
+ actual = _active_write_scope()
282
+ if actual != required_scope:
283
+ banner = _format_coordinator_banner(
284
+ token, "write", required_scope, actual
285
+ )
286
+ vcprint(banner, color="red", log_level="ERROR")
287
+ logger.error("CoordinatorWriteViolation: %s", banner)
288
+ return
289
+ # Governed contexts — always allowed:
290
+ if _has_active_open_session():
125
291
  return
126
292
  try:
127
293
  from matrx_orm.core.transaction import get_active_connection
@@ -131,10 +297,6 @@ def assert_managed_write_allowed(query: str) -> None:
131
297
  except ImportError:
132
298
  pass
133
299
 
134
- m = _WRITE_TARGET_RE.match(query)
135
- if not m:
136
- return
137
- token = m.group(1).strip().strip('"')
138
300
  if token in _STRICT_TABLES or token.split(".")[-1].strip('"') in _STRICT_TABLES:
139
301
  banner = _format_chokepoint_banner(token)
140
302
  vcprint(banner, color="red")
@@ -146,6 +308,33 @@ def assert_managed_write_allowed(query: str) -> None:
146
308
  raise ManagedWriteViolation(token, "write (ungoverned)")
147
309
 
148
310
 
311
+ def _active_write_scope() -> str | None:
312
+ for candidate in reversed(session_stack()):
313
+ if candidate.phase is SessionPhase.OPEN:
314
+ return candidate.write_scope
315
+ return None
316
+
317
+
318
+ def _model_for_coordinator_table(token: str, bare_token: str) -> type | None:
319
+ from matrx_orm.core.registry import ModelRegistry
320
+
321
+ for model_cls in ModelRegistry.all_models().values():
322
+ meta = getattr(model_cls, "_meta", None)
323
+ if meta is None:
324
+ continue
325
+ names = {
326
+ str(name).strip('"')
327
+ for name in (
328
+ getattr(meta, "qualified_table_name", None),
329
+ getattr(meta, "table_name", None),
330
+ )
331
+ if name
332
+ }
333
+ if token in names or bare_token in names:
334
+ return model_cls
335
+ return None
336
+
337
+
149
338
  def is_strict_table(model_cls: type) -> bool:
150
339
  """True if ``model_cls``'s table is registered ``strict`` (execute_write-guarded).
151
340
 
@@ -235,6 +424,13 @@ def _apply_wrapping(model_cls: type, mode: str) -> None:
235
424
  # Register for the universal execute_write guard so update/delete/
236
425
  # QueryBuilder/raw-SQL paths are enforced too, not just create/bulk_*.
237
426
  _register_strict_table(model_cls)
427
+ elif mode == "coordinator":
428
+ required_scope = getattr(
429
+ getattr(model_cls, "_meta", None),
430
+ "managed_write_scope",
431
+ "coordinator",
432
+ )
433
+ _register_coordinator_table(model_cls, str(required_scope))
238
434
  for method_name in _WRITE_METHODS:
239
435
  original = getattr(model_cls, method_name, None)
240
436
  if original is None:
@@ -251,6 +447,7 @@ def declare_managed(
251
447
  model_cls: type,
252
448
  *,
253
449
  mode: str = "elevate",
450
+ write_scope: str = "coordinator",
254
451
  watched_lifecycle: dict | None = None,
255
452
  ) -> None:
256
453
  """Programmatic equivalent of declaring ``Meta.managed_writes`` /
@@ -281,15 +478,19 @@ def declare_managed(
281
478
 
282
479
  Args:
283
480
  model_cls: a matrx-orm Model class.
284
- mode: ``"elevate"`` (default — auto-open one-shot Session if
285
- none is active) or ``"strict"`` (raise
286
- ``ManagedWriteViolation`` outside any Session).
481
+ mode: ``"elevate"`` (auto-open a one-shot), ``"strict"`` (require
482
+ any governed Session), or ``"coordinator"`` (require the named
483
+ owner Session and reject ordinary Session-of-one writes).
484
+ write_scope: authority required by coordinator mode.
287
485
  watched_lifecycle: optional dict matching the
288
486
  :class:`WatchedLifecycleConfig` fields (``predicate``,
289
487
  ``max_age_secs``, ``transition_to_on_timeout``, etc.).
290
488
  """
291
- if mode not in ("strict", "elevate"):
292
- raise ValueError(f"declare_managed: mode must be 'strict' or 'elevate', got {mode!r}")
489
+ if mode not in ("strict", "elevate", "coordinator"):
490
+ raise ValueError(
491
+ "declare_managed: mode must be 'strict', 'elevate', or "
492
+ f"'coordinator', got {mode!r}"
493
+ )
293
494
  meta = getattr(model_cls, "_meta", None)
294
495
  if meta is None:
295
496
  raise ValueError(
@@ -304,6 +505,10 @@ def declare_managed(
304
505
  object.__setattr__(meta, "managed_writes", mode)
305
506
 
306
507
  _apply_wrapping(model_cls, mode)
508
+ if mode == "coordinator":
509
+ if not write_scope.strip():
510
+ raise ValueError("declare_managed coordinator mode requires write_scope")
511
+ _register_coordinator_table(model_cls, write_scope.strip())
307
512
 
308
513
  if watched_lifecycle is not None:
309
514
  try:
@@ -337,7 +542,7 @@ def _managed_mode(model_cls: type) -> str | None:
337
542
  return None
338
543
  if declared is True:
339
544
  return "elevate"
340
- if isinstance(declared, str) and declared in ("strict", "elevate"):
545
+ if isinstance(declared, str) and declared in ("strict", "elevate", "coordinator"):
341
546
  return declared
342
547
  logger.warning(
343
548
  "managed_writes on %s must be True | 'strict' | 'elevate' — got %r. Treating as 'elevate'.",
@@ -358,6 +563,7 @@ def _wrap_classmethod(model_cls: type, method_name: str, original: Any, mode: st
358
563
 
359
564
  @functools.wraps(original_func)
360
565
  async def wrapped(cls, *args, **kwargs):
566
+ active_mode = _managed_mode(cls) or mode
361
567
  # Reentrancy guard: when the flush executor calls bulk_create /
362
568
  # bulk_update / bulk_delete to actually write rows, those calls
363
569
  # must pass through unchanged. Without this check we'd recurse
@@ -381,6 +587,8 @@ def _wrap_classmethod(model_cls: type, method_name: str, original: Any, mode: st
381
587
  session = candidate
382
588
  break
383
589
  if session is not None:
590
+ if active_mode == "coordinator":
591
+ assert_model_write_allowed(cls, method_name, session=session)
384
592
  # Defer to the active Session.
385
593
  return await _defer_to_session(
386
594
  session,
@@ -392,7 +600,10 @@ def _wrap_classmethod(model_cls: type, method_name: str, original: Any, mode: st
392
600
  )
393
601
 
394
602
  # No active Session.
395
- if mode == "strict":
603
+ if active_mode == "coordinator":
604
+ assert_model_write_allowed(cls, method_name)
605
+
606
+ if active_mode == "strict":
396
607
  banner = _format_violation_banner(cls.__name__, method_name)
397
608
  vcprint(banner, color="red")
398
609
  logger.error(
@@ -565,6 +776,33 @@ def _format_chokepoint_banner(table: str) -> str:
565
776
  )
566
777
 
567
778
 
779
+ def _format_coordinator_banner(
780
+ model_name: str,
781
+ op_kind: str,
782
+ required_scope: str,
783
+ actual_scope: str | None,
784
+ ) -> str:
785
+ return (
786
+ "\n"
787
+ "████████████████████████████████████████████████████████████████████\n"
788
+ "██ COORDINATOR OWNERSHIP BYPASS — WRITE CONTINUED ██\n"
789
+ "████████████████████████████████████████████████████████████████████\n"
790
+ f" Model : {model_name}\n"
791
+ f" Operation : {op_kind}\n"
792
+ f" Required : Session(write_scope={required_scope!r})\n"
793
+ f" Actual : Session(write_scope={actual_scope!r})\n"
794
+ " Result : the write was NOT blocked, but bypassed its owner.\n"
795
+ " Fix : queue this write through the registered Coordinator.\n"
796
+ " Override : allow_direct_coordinator_write(\n"
797
+ " Model, reason='...',\n"
798
+ " acknowledgement=\n"
799
+ f" {COORDINATOR_BYPASS_ACKNOWLEDGEMENT!r},\n"
800
+ " )\n"
801
+ " is reserved for intentional out-of-request writes.\n"
802
+ "████████████████████████████████████████████████████████████████████\n"
803
+ )
804
+
805
+
568
806
  def _format_violation_banner(model_name: str, op_kind: str) -> str:
569
807
  return (
570
808
  "\n"