matrx-orm 3.1.255__tar.gz → 3.1.256__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 (460) hide show
  1. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/CLAUDE.md +13 -24
  2. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/PKG-INFO +1 -1
  3. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/__init__.py +2 -0
  4. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/platform_access.py +10 -1
  5. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/query/FEATURE.md +20 -36
  6. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/query/builder.py +12 -3
  7. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/query/executor.py +10 -6
  8. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/pyproject.toml +1 -1
  9. matrx_orm-3.1.256/tests/level1/test_row_lock_modes.py +138 -0
  10. matrx_orm-3.1.256/tests/test_platform_access_strict.py +38 -0
  11. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/.gitignore +0 -0
  12. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/AGENTS.md +0 -0
  13. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/BASE_CLASS_METHODS.md +0 -0
  14. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/CHANGELOG.md +0 -0
  15. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/MODEL_API.md +0 -0
  16. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/README.md +0 -0
  17. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/database/orm/extended/managers/ai_model_base.py +0 -0
  18. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/docs/migrations.md +0 -0
  19. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/FEATURE.md +0 -0
  20. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/MODULE_README.md +0 -0
  21. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/adapters/MODULE_README.md +0 -0
  22. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/adapters/__init__.py +0 -0
  23. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/adapters/async_postgresql.py +0 -0
  24. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/adapters/base_adapter.py +0 -0
  25. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  26. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/adapters/supabase_adapter.py +0 -0
  27. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/admin/FEATURE.md +0 -0
  28. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/admin/__init__.py +0 -0
  29. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/admin/router.py +0 -0
  30. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/api/__init__.py +0 -0
  31. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/api/auth.py +0 -0
  32. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/api/config.py +0 -0
  33. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/api/handlers.py +0 -0
  34. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/api/protocol.py +0 -0
  35. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/api/server.py +0 -0
  36. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/associations.py +0 -0
  37. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/cache_debug.py +0 -0
  38. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/catalog.py +0 -0
  39. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/catalog_sql.py +0 -0
  40. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/client/MODULE_README.md +0 -0
  41. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/client/__init__.py +0 -0
  42. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/client/postgres_connection.py +0 -0
  43. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/client/postgrest.py +0 -0
  44. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/client/supabase_auth.py +0 -0
  45. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/client/supabase_config.py +0 -0
  46. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/client/supabase_manager.py +0 -0
  47. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  48. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/FEATURE.md +0 -0
  49. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/MODULE_README.md +0 -0
  50. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  51. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/TASKS.md +0 -0
  52. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/__init__.py +0 -0
  53. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/admin_lane.py +0 -0
  54. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/async_db_manager.py +0 -0
  55. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/base.py +0 -0
  56. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/config.py +0 -0
  57. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/connections.py +0 -0
  58. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/db_function.py +0 -0
  59. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/diagnostics.py +0 -0
  60. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/expressions.py +0 -0
  61. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/extended.py +0 -0
  62. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/fields.py +0 -0
  63. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/foreign_session_state.py +0 -0
  64. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/introspection.py +0 -0
  65. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/listen.py +0 -0
  66. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/loop_filters.py +0 -0
  67. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/model_dto.py +0 -0
  68. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/model_view.py +0 -0
  69. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/one_database.py +0 -0
  70. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/paginator.py +0 -0
  71. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/pool_maintenance.py +0 -0
  72. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/pool_watch.py +0 -0
  73. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/pooler_role.py +0 -0
  74. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/pydantic_bridge.py +0 -0
  75. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/read_write.py +0 -0
  76. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/registry.py +0 -0
  77. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/relations.py +0 -0
  78. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/resilience.py +0 -0
  79. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/rls_session.py +0 -0
  80. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/session_context.py +0 -0
  81. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/signals.py +0 -0
  82. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/statement_ceiling.py +0 -0
  83. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/tls.py +0 -0
  84. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/transaction.py +0 -0
  85. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/types.py +0 -0
  86. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/write_queue.py +0 -0
  87. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/core/write_retry.py +0 -0
  88. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/entity.py +0 -0
  89. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/error_handling.py +0 -0
  90. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/exceptions.py +0 -0
  91. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/extended/__init__.py +0 -0
  92. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/extended/app_error_handler.py +0 -0
  93. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/identity_rule.py +0 -0
  94. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/local_sqlite.py +0 -0
  95. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/middleware/__init__.py +0 -0
  96. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/middleware/base.py +0 -0
  97. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/MODULE_README.md +0 -0
  98. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/__init__.py +0 -0
  99. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/cli.py +0 -0
  100. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/ddl.py +0 -0
  101. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/diff.py +0 -0
  102. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/executor.py +0 -0
  103. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/integration.py +0 -0
  104. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/loader.py +0 -0
  105. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/operations.py +0 -0
  106. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/state.py +0 -0
  107. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/migrations/table_filter.py +0 -0
  108. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/no_org_assignment.py +0 -0
  109. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/FEATURE.md +0 -0
  110. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/MODULE_README.md +0 -0
  111. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/__init__.py +0 -0
  112. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/bulk_update_values.py +0 -0
  113. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/catalog_select.py +0 -0
  114. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/conflict.py +0 -0
  115. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/conflict_writes.py +0 -0
  116. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/create.py +0 -0
  117. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/db_functions.py +0 -0
  118. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/delete.py +0 -0
  119. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/dynamic_admin.py +0 -0
  120. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/dynamic_crud.py +0 -0
  121. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/graph_walk.py +0 -0
  122. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/latest_rows.py +0 -0
  123. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/queue_claim.py +0 -0
  124. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/read.py +0 -0
  125. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/relational_backfill.py +0 -0
  126. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/staging_load.py +0 -0
  127. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/operations/update.py +0 -0
  128. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/platform_db.py +0 -0
  129. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/py.typed +0 -0
  130. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/pytest_lock_guard.py +0 -0
  131. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/pytest_plugin.py +0 -0
  132. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/python_sql/MODULE_README.md +0 -0
  133. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/python_sql/__init__.py +0 -0
  134. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/python_sql/db_objects.py +0 -0
  135. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  136. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  137. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/query/MODULE_README.md +0 -0
  138. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/query/__init__.py +0 -0
  139. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/resources/__init__.py +0 -0
  140. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
  141. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
  142. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/FEATURE.md +0 -0
  143. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  144. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/__init__.py +0 -0
  145. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/code_handler.py +0 -0
  146. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  147. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/columns.py +0 -0
  148. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/common.py +0 -0
  149. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/diff_preview.py +0 -0
  150. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  151. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/excluded_targets.py +0 -0
  152. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/generator.py +0 -0
  153. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  154. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  155. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  156. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  157. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/package_wiring.py +0 -0
  158. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/relationships.py +0 -0
  159. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/runner.py +0 -0
  160. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/schema.py +0 -0
  161. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/schema_manager.py +0 -0
  162. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/tables.py +0 -0
  163. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/schema_builder/views.py +0 -0
  164. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/FEATURE.md +0 -0
  165. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/__init__.py +0 -0
  166. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/attachments.py +0 -0
  167. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/backup_restore.py +0 -0
  168. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/crypto.py +0 -0
  169. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  170. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/items.py +0 -0
  171. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/model.py +0 -0
  172. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
  173. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/native_enable.py +0 -0
  174. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
  175. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
  176. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/native_source.py +0 -0
  177. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/org_service.py +0 -0
  178. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/org_vault_gate.py +0 -0
  179. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/service.py +0 -0
  180. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/use_stamp.py +0 -0
  181. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/secrets_battery/vault_history.py +0 -0
  182. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/README.md +0 -0
  183. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/__init__.py +0 -0
  184. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/coalesce.py +0 -0
  185. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/dag.py +0 -0
  186. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/errors.py +0 -0
  187. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/fallback.py +0 -0
  188. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/flush.py +0 -0
  189. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/lifecycle.py +0 -0
  190. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/managed.py +0 -0
  191. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/op.py +0 -0
  192. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/reads.py +0 -0
  193. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/session.py +0 -0
  194. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/session/telemetry.py +0 -0
  195. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  196. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/sql_executor/__init__.py +0 -0
  197. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/sql_executor/executor.py +0 -0
  198. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/sql_executor/queries.py +0 -0
  199. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/sql_executor/registry.py +0 -0
  200. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/sql_executor/types.py +0 -0
  201. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/sql_executor/utils.py +0 -0
  202. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/state.py +0 -0
  203. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/utils/__init__.py +0 -0
  204. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/utils/sql_utils.py +0 -0
  205. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/utils/type_converters.py +0 -0
  206. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/matrx_orm/write_actor.py +0 -0
  207. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/scripts/git-branches.sh +0 -0
  208. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/scripts/publish.sh +0 -0
  209. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/scripts/release.sh +0 -0
  210. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/scripts/verify_wheel.py +0 -0
  211. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/MODULE_README.md +0 -0
  212. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/__init__.py +0 -0
  213. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/conftest.py +0 -0
  214. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/MODULE_README.md +0 -0
  215. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/__init__.py +0 -0
  216. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_adapter_registry.py +0 -0
  217. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_admin_db_columns.py +0 -0
  218. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_admin_response_rendering.py +0 -0
  219. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_admin_search_negation.py +0 -0
  220. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_admin_search_sentinels.py +0 -0
  221. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_advisory_lock.py +0 -0
  222. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_agent_message.py +0 -0
  223. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_api_auth.py +0 -0
  224. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_api_config.py +0 -0
  225. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_api_handlers.py +0 -0
  226. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_api_protocol.py +0 -0
  227. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_array_agg_order_by.py +0 -0
  228. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_association_trigger_actor_naming.py +0 -0
  229. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_associations.py +0 -0
  230. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_associations_wiring.py +0 -0
  231. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_borrowed_role_never_reaches_server_calls.py +0 -0
  232. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_bulk_hydration_offload.py +0 -0
  233. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_bulk_update_by_pk.py +0 -0
  234. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  235. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_cache_debug.py +0 -0
  236. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_call_function.py +0 -0
  237. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_case_when.py +0 -0
  238. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_catalog.py +0 -0
  239. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_column_scoped_writes.py +0 -0
  240. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_committed_row_guard.py +0 -0
  241. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_composite_pk_filter.py +0 -0
  242. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_composite_pk_write_paths.py +0 -0
  243. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_config.py +0 -0
  244. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_conflict_target.py +0 -0
  245. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_conflict_writes.py +0 -0
  246. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_connection_codecs.py +0 -0
  247. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_connection_poison_guard.py +0 -0
  248. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_connection_primitives.py +0 -0
  249. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_count_composite_distinct.py +0 -0
  250. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_count_limit_param_leak.py +0 -0
  251. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_db_function_field.py +0 -0
  252. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_ddl_generator.py +0 -0
  253. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_deadlock_retry.py +0 -0
  254. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_derived_table.py +0 -0
  255. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_dirty_tracking.py +0 -0
  256. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_dynamic_crud.py +0 -0
  257. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_exceptions.py +0 -0
  258. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  259. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_expression_primitives.py +0 -0
  260. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_fetch_on_init_holds_whole_table.py +0 -0
  261. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_fields.py +0 -0
  262. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  263. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_fk_cycle_resolution.py +0 -0
  264. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_from_alias.py +0 -0
  265. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_fts.py +0 -0
  266. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_func_expression_args.py +0 -0
  267. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_graph_walk.py +0 -0
  268. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_guarded_read_never_reruns_empty.py +0 -0
  269. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_having_annotate_alias.py +0 -0
  270. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  271. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_introspect_rls_policies.py +0 -0
  272. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_join_and_aggregate.py +0 -0
  273. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_latest_rows.py +0 -0
  274. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_listen_notify.py +0 -0
  275. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_local_sqlite.py +0 -0
  276. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_lock_contention_retry.py +0 -0
  277. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_managed_write_guard.py +0 -0
  278. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_matrx_entity.py +0 -0
  279. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_migration_diff_types.py +0 -0
  280. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_migration_loader.py +0 -0
  281. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_model_instance.py +0 -0
  282. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_model_meta.py +0 -0
  283. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_optimistic_concurrency.py +0 -0
  284. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_order_by_expression.py +0 -0
  285. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_org_assignment_inventory.py +0 -0
  286. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
  287. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_parameter_name_hydration.py +0 -0
  288. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_patch_jsonb_path.py +0 -0
  289. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_percentile_cont.py +0 -0
  290. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_persisted_count.py +0 -0
  291. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_person_scoped_session_bypasses_shared_cache.py +0 -0
  292. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_pool_create_never_wedges.py +0 -0
  293. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_pool_holder_census.py +0 -0
  294. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_pool_loop_recreation.py +0 -0
  295. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_pool_maintenance.py +0 -0
  296. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_postgrest_filters.py +0 -0
  297. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_pydantic_bridge.py +0 -0
  298. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_query_builder.py +0 -0
  299. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_query_error_detail.py +0 -0
  300. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_query_executor_sql.py +0 -0
  301. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_query_timeout.py +0 -0
  302. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  303. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_queue_claim.py +0 -0
  304. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_read_connection_retry.py +0 -0
  305. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_read_write_declared.py +0 -0
  306. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_registry.py +0 -0
  307. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_registry_multi_database.py +0 -0
  308. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_registry_multi_schema.py +0 -0
  309. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_registry_reads_outside_the_person_session.py +0 -0
  310. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_relational_backfill.py +0 -0
  311. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_relations.py +0 -0
  312. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_resilience.py +0 -0
  313. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_rls_session_scope.py +0 -0
  314. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_schema_exists.py +0 -0
  315. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_session_advisory_lock.py +0 -0
  316. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_standalone_write_session_context.py +0 -0
  317. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_state_cache.py +0 -0
  318. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_subquery_filter_raw.py +0 -0
  319. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_subquery_in_filter.py +0 -0
  320. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_subquery_nested_expressions.py +0 -0
  321. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_supabase_auth.py +0 -0
  322. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_supabase_config.py +0 -0
  323. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_sync_pool_declares_read_write.py +0 -0
  324. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_tls.py +0 -0
  325. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_transaction_database_identity.py +0 -0
  326. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_transaction_gucs_one_round_trip.py +0 -0
  327. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_transaction_isolation.py +0 -0
  328. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_transaction_pool_release_sends_nothing.py +0 -0
  329. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_transaction_top_level_cleanup.py +0 -0
  330. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_typed_join_projection.py +0 -0
  331. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_typed_predicates.py +0 -0
  332. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_update_case_expression.py +0 -0
  333. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_update_subquery.py +0 -0
  334. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_update_with_rebase.py +0 -0
  335. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_upsert_default_leak.py +0 -0
  336. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_upsert_with_conflict.py +0 -0
  337. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_values_jsonb_decode.py +0 -0
  338. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_watched_lifecycle.py +0 -0
  339. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_wheel_contents.py +0 -0
  340. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_write_queue_deadline.py +0 -0
  341. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level1/test_write_retry_semantics.py +0 -0
  342. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/MODULE_README.md +0 -0
  343. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/__init__.py +0 -0
  344. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/conftest.py +0 -0
  345. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_bulk_ops.py +0 -0
  346. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_cache_integration.py +0 -0
  347. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_crud.py +0 -0
  348. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_foreign_keys.py +0 -0
  349. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_m2m.py +0 -0
  350. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_manager.py +0 -0
  351. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_migrations_live.py +0 -0
  352. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_query_execution.py +0 -0
  353. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/test_schema_diff.py +0 -0
  354. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
  355. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/live_pooler_role.py +0 -0
  356. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/native_source_cases.py +0 -0
  357. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/native_source_oracle.rs +0 -0
  358. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project/.env.example +0 -0
  359. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project/README.md +0 -0
  360. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project/__init__.py +0 -0
  361. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project/generate.py +0 -0
  362. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project/generated/.gitkeep +0 -0
  363. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project/matrx_orm.yaml +0 -0
  364. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project/test_schema_generation.py +0 -0
  365. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project_desktop/.env.example +0 -0
  366. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project_desktop/README.md +0 -0
  367. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project_desktop/__init__.py +0 -0
  368. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project_desktop/client_example.py +0 -0
  369. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project_desktop/client_example.ts +0 -0
  370. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  371. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/sample_project_desktop/server.py +0 -0
  372. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/entity_tests.py +0 -0
  373. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_base_generation.py +0 -0
  374. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  375. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_composite_unique_generation.py +0 -0
  376. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_dto_identity_generation.py +0 -0
  377. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_entity_capability_generation.py +0 -0
  378. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_enum_ordering.py +0 -0
  379. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_excluded_fk_targets.py +0 -0
  380. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_generate_schema.py +0 -0
  381. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_junction_analysis.py +0 -0
  382. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_multi_schema_output.py +0 -0
  383. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_schema_analysis.py +0 -0
  384. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_sql_expression_defaults.py +0 -0
  385. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_table_relationships_query.py +0 -0
  386. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema/test_view_generation.py +0 -0
  387. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema_builder/test_array_default_parsing.py +0 -0
  388. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema_builder/test_enum_array_column.py +0 -0
  389. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  390. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  391. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/schema_builder/test_never_filter_columns.py +0 -0
  392. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/__init__.py +0 -0
  393. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/conftest.py +0 -0
  394. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_a_failed_capture_still_names_the_failure.py +0 -0
  395. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_a_grouped_update_writes_only_each_rows_own_fields.py +0 -0
  396. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_cache_no_downgrade.py +0 -0
  397. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_capture_poison_proof.py +0 -0
  398. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_coalesce.py +0 -0
  399. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_dag.py +0 -0
  400. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_disk_spill_fallback.py +0 -0
  401. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_flush_individual_fallback.py +0 -0
  402. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_governed_write.py +0 -0
  403. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_managed.py +0 -0
  404. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_op_enqueue_site.py +0 -0
  405. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_pending_insert_honors_operator_filters.py +0 -0
  406. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_reads.py +0 -0
  407. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_record_error_bounded_insert.py +0 -0
  408. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_record_error_group_open.py +0 -0
  409. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_record_error_storm.py +0 -0
  410. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_session.py +0 -0
  411. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_spill_guard_plugin.py +0 -0
  412. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/session/test_tests_never_write_live_system_error.py +0 -0
  413. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_a_backend_handed_over_mid_transaction_never_takes_our_writes.py +0 -0
  414. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_a_bulk_cache_never_stalls_the_loop.py +0 -0
  415. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_a_failed_use_stamp_never_fails_the_read.py +0 -0
  416. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_a_keyword_column_refuses_generation.py +0 -0
  417. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_a_list_of_uuids_binds_as_uuid_array.py +0 -0
  418. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_a_read_only_backend_never_refuses_our_writes.py +0 -0
  419. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_a_sealed_field_shows_no_character_of_its_value.py +0 -0
  420. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_array_arg_element_type.py +0 -0
  421. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_attachment_mutation_serialization.py +0 -0
  422. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_backup_alias_conflict.py +0 -0
  423. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_backup_history_restore.py +0 -0
  424. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_backup_restore_live.py +0 -0
  425. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_call_function_as.py +0 -0
  426. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_call_function_none_mode.py +0 -0
  427. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_composite_fk_is_not_a_column_fk.py +0 -0
  428. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_database_name_alias.py +0 -0
  429. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_db_functions.py +0 -0
  430. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_dsn_password_masking.py +0 -0
  431. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_dynamic_select_unregistered.py +0 -0
  432. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_first_asks_the_database_for_one_row.py +0 -0
  433. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_foreign_session_state.py +0 -0
  434. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_immutable_insert_or_exact_verify.py +0 -0
  435. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_instance_update_runs_expressions.py +0 -0
  436. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_jsonb_postgres_illegal_text.py +0 -0
  437. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_keyed_digest.py +0 -0
  438. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_model_cls_refactor.py +0 -0
  439. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_native_enable.py +0 -0
  440. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_native_lifecycle.py +0 -0
  441. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_native_lifecycle_fences.py +0 -0
  442. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_native_passkey_receipts.py +0 -0
  443. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_native_source.py +0 -0
  444. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  445. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_one_database.py +0 -0
  446. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_order_by_desc_nulls_last.py +0 -0
  447. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_org_vault_gate.py +0 -0
  448. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_package_wiring_views.py +0 -0
  449. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_pooler_role_guard.py +0 -0
  450. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_secret_audit_names_its_organization.py +0 -0
  451. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_secrets_battery.py +0 -0
  452. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_secrets_minting_keys.py +0 -0
  453. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_shared_lock_guard.py +0 -0
  454. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_sql_param_casts.py +0 -0
  455. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_state_cache_sweeps_stale.py +0 -0
  456. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_statement_ceiling_warning.py +0 -0
  457. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_sync_twins_guard.py +0 -0
  458. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_update_result_truthiness.py +0 -0
  459. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_user_visible_lookup.py +0 -0
  460. {matrx_orm-3.1.255 → matrx_orm-3.1.256}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
@@ -11,11 +11,11 @@
11
11
 
12
12
  ## Read this first
13
13
 
14
- matrx-orm is a standalone async Postgres ORM (`Model`, `QueryBuilder`, migrations, admin router, multi-project registry). It has been the cleanest of the Matrx packages and we intend to keep it that way. Before adding *any* import, verify it resolves in a fresh venv that only has matrx-orm + matrx-utils.
14
+ matrx-orm is a standalone async Postgres ORM (`Model`, `QueryBuilder`, migrations, admin router, multi-project registry). Before adding *any* import, verify it resolves in a fresh venv that only has matrx-orm + matrx-utils.
15
15
 
16
16
  ### Row identity
17
17
 
18
- Every table that becomes a Model needs row identity: a single-column primary key (usually `id`) or a composite primary key. Intentionally PK-less tables (backup, staging, scratch) belong in the host's `matrx_orm.yaml` `exclude_tables` — do not invent a PK. Tables named with an archival prefix (`_bak_*` / `_backup_*`) are auto-skipped at schema discovery (`is_archival_table_name`) and never need an exclude entry — a live backup table can no longer kill a full generation run. Never add a surrogate `id` only to satisfy codegen when a natural or composite key already exists. Canonical copy + error text: [`matrx_orm/identity_rule.py`](matrx_orm/identity_rule.py).
18
+ Every table that becomes a Model needs row identity: a single-column primary key (usually `id`) or a composite primary key. Intentionally PK-less tables (backup, staging, scratch) belong in the host's `matrx_orm.yaml` `exclude_tables` — do not invent a PK. Tables named with an archival prefix (`_bak_*` / `_backup_*`) are auto-skipped at schema discovery (`is_archival_table_name`) and never need an exclude entry. Never add a surrogate `id` only to satisfy codegen when a natural or composite key already exists. Canonical copy + error text: [`matrx_orm/identity_rule.py`](matrx_orm/identity_rule.py).
19
19
 
20
20
  ---
21
21
 
@@ -24,12 +24,12 @@ Every table that becomes a Model needs row identity: a single-column primary key
24
24
  - **Config / project registry** (`matrx_orm.core.config`): `DatabaseProjectConfig`, `register_database`, `register_database_from_env`, `get_database_config`, `get_manager_config`, `DatabaseRegistry`. A single process can run multiple named database projects concurrently.
25
25
  - 🚨 **ONE CONNECTION, REQUIRED** (`matrx_orm.platform_db`): `register_platform_db(name, ...)` + `platform_connection_url()` — the ONLY sanctioned way any Matrx service or package turns env vars into a pool. One set of variables (`SUPABASE_MATRIX_*`), owned here; TLS is a deployment input (`SUPABASE_MATRIX_SSL`), never hardcoded; **RAISES** if it isn't given a database — no degraded mode. Every package's `bootstrap_db()` is one call to it. A package may absolutely run on its OWN Postgres — that is a change of VALUES, never a new variable name; what it may never have is a *second candidate* or a fallback chain. `register_database_from_env` is the low-level primitive underneath — **application and package code must not call it directly** (guard: `scripts/check_one_database.py`, blocking in release).
26
26
  - **Core model layer**: `Model`, `BaseManager`, `BaseDTO`, `ModelView`, `model_registry`, plus 50+ field types (`CharField`, `IntegerField`, `ForeignKey`, `ManyToManyField`, `JSONField`, …).
27
- - **`MatrxEntity`** (`matrx_orm/entity.py`) — **the platform entity contract as an ORM primitive.** A table registered in `platform.entity_types` generates as a `MatrxEntity` (everything else stays a plain `Model`) and gets the behavior instead of every consumer hand-wiring it: `await page.versions()` (not a query on `history.row_versions`), `await page.restore(3)` (not a `version_restore` RPC + a cache bust — `restore()` busts the cache itself), `Note.alive()` / `.dead()` / `.soft_delete()` / `.undelete()`, and `Page.capabilities()`. Capability flags (`_entity_token`, `_is_versioned`, `_has_soft_delete`, `_is_org_scoped`, `_rls_variant`) are **stamped by the generator from the LIVE DATABASE** (does the table really carry the `_version_capture` trigger? a `deleted_at` column?) — never from `entity_types`' own flags, which are wrong on 61 of its 242 rows. **The database is truth; a flag is a claim.** Using a capability an entity does not declare RAISES (`EntityCapabilityError`) and names the fix — it never returns silently-wrong data, and `is_deleted` refuses to answer from a column a partial fetch never loaded. Drift guard: `scripts/check_entity_drift.py` (loud, non-blocking, in `release.sh`).
27
+ - **`MatrxEntity`** (`matrx_orm/entity.py`) — **the platform entity contract as an ORM primitive.** A table registered in `platform.entity_types` generates as a `MatrxEntity` (everything else stays a plain `Model`) and gets the behavior instead of every consumer hand-wiring it: `await page.versions()` (not a query on `history.row_versions`), `await page.restore(3)` (not a `version_restore` RPC + a cache bust — `restore()` busts the cache itself), `Note.alive()` / `.dead()` / `.soft_delete()` / `.undelete()`, and `Page.capabilities()`. Capability flags (`_entity_token`, `_is_versioned`, `_has_soft_delete`, `_is_org_scoped`, `_rls_variant`) are **stamped by the generator from the LIVE DATABASE** (does the table really carry the `_version_capture` trigger? a `deleted_at` column?) — never from `entity_types`' own flags. **The database is truth; a flag is a claim.** Using a capability an entity does not declare RAISES (`EntityCapabilityError`) and names the fix — it never returns silently-wrong data, and `is_deleted` refuses to answer from a column a partial fetch never loaded. Drift guard: `scripts/check_entity_drift.py` (loud, non-blocking, in `release.sh`).
28
28
  - **Optimistic concurrency (`expected_version=`)** — opt-in compare-and-swap on the canonical `version` int column: `update_where` / `update_item` / instance `update()` take `expected_version=N` (`WHERE version = N`, `SET version = N + 1`; 0 rows → `DoesNotExist` if gone, `OptimisticLockError` if changed — carrying the **decision package**: `current_version`, `current_row`, `contested_fields` (current values of ONLY the fields this write attempted), and `changed_by`/`changed_at` from `history.row_versions` where versioned); `bulk_update(..., check_version=True)` guards per row; `MatrxEntity.update_with_rebase` auto-rebases past DISJOINT concurrent writes using `history.row_versions` snapshots. **Never automatic** — an unguarded write stays last-write-wins (the `_touch_row` trigger bumps `version` on every update, so auto-guarding would raise spurious conflicts). Contract: `common-docs/systems/architecture/optimistic-concurrency/FEATURE.md`.
29
- - **Change tracking** — every instance records which columns were **loaded** (a SELECTed row, or the caller's kwargs) and which were **changed**. `save()` writes only those, so `.only("id","email")` + `save()` can never NULL the columns it never fetched (it used to — silent data loss). Mutable containers (jsonb/arrays) are always rewritten when loaded, because `obj.metadata["k"]=1` cannot be seen by `__setattr__`; **reassigning** (`obj.metadata = {...}`) is tracked precisely and is the sanctioned way. `Model._hydrate()` is the ONE row→model entry point.
30
- - **Upsert writes only what the caller supplied.** `upsert` / `bulk_upsert` / `upsert_with_conflict` materialize every field's Python default so the **INSERT** half is complete — but `ON CONFLICT DO UPDATE SET` covers **only the columns present in the caller's `data`** (an explicit `update_fields` still wins). Letting defaults reach the UPDATE half silently RESET every defaulted column on each re-upsert of an existing row: it once reset scraped-and-analyzed `research.rs_source` rows to `scrape_status='pending', server_attempts=0` (a watchdog then finalized them `'failed'`) and undid users' manual `is_included=false`. A caller-supplied `None` is excluded too — it is dropped from the INSERT, and `EXCLUDED.<col>` on an omitted column resolves to that column's **DEFAULT**. An upsert of only conflict keys means "ensure this row exists" and reconciles to a no-op self-assignment, never a raise (a bare `DO NOTHING` would break `RETURNING *`). Pinned by `tests/level1/test_upsert_default_leak.py`.
29
+ - **Change tracking** — every instance records which columns were **loaded** (a SELECTed row, or the caller's kwargs) and which were **changed**. `save()` writes only those, so `.only("id","email")` + `save()` can never NULL the columns it never fetched. Mutable containers (jsonb/arrays) are always rewritten when loaded, because `obj.metadata["k"]=1` cannot be seen by `__setattr__`; **reassigning** (`obj.metadata = {...}`) is tracked precisely and is the sanctioned way. `Model._hydrate()` is the ONE row→model entry point.
30
+ - **Upsert writes only what the caller supplied.** `upsert` / `bulk_upsert` / `upsert_with_conflict` materialize every field's Python default so the **INSERT** half is complete — but `ON CONFLICT DO UPDATE SET` covers **only the columns present in the caller's `data`** (an explicit `update_fields` still wins). A caller-supplied `None` is excluded too — it is dropped from the INSERT, and `EXCLUDED.<col>` on an omitted column resolves to that column's **DEFAULT**. An upsert of only conflict keys means "ensure this row exists" and reconciles to a no-op self-assignment, never a raise (a bare `DO NOTHING` would break `RETURNING *`). Pinned by `tests/level1/test_upsert_default_leak.py`.
31
31
  - **Read-only models** (`_read_only = True`): a model that can be queried but NEVER written through the ORM — every write path raises `ReadOnlyModelError` (enforced at `QueryExecutor.insert/bulk_insert/upsert/update/delete`, so it covers all of `create`/`save`/`update`/`delete`/`update_where`/`delete_where`/`upsert`/`bulk_*`). Used for DB-managed tables the app must not mutate. The canonical **`auth.users`** model is baked into the schema generator (`get_string_user_model`) as the full, curated Supabase auth schema (safe columns only — no password/token fields), `_read_only`, bound per-database. Read a user's profile/metadata there; mutate users only through Supabase Auth (GoTrue).
32
- - **Connection-level type codecs** (`async_db_manager._init_connection_default`): every pooled connection decodes **uuid → `str`**, **vector → `list[float]`**, and **json/jsonb → `dict`/`list`** at the driver boundary — the TYPE decides the decode, never the call path. So `call_function` scalar/rows, raw fetches, and field hydration all agree; a caller must **never `json.loads` a fetched json/jsonb value** (it's already decoded; the 2026-07-13 `build_agent_context` crash was a jsonb RPC fetched pre-codec as a raw string). Encoding: a pre-serialised JSON `str` passes through untouched; a native dict/list is dumped (`_encode_json`). Pinned by `tests/level1/test_connection_codecs.py`.
32
+ - **Connection-level type codecs** (`async_db_manager._init_connection_default`): every pooled connection decodes **uuid → `str`**, **vector → `list[float]`**, and **json/jsonb → `dict`/`list`** at the driver boundary — the TYPE decides the decode, never the call path. So `call_function` scalar/rows, raw fetches, and field hydration all agree; a caller must **never `json.loads` a fetched json/jsonb value** (it's already decoded). Encoding: a pre-serialised JSON `str` passes through untouched; a native dict/list is dumped (`_encode_json`). Pinned by `tests/level1/test_connection_codecs.py`.
33
33
  - **Connection pools are event-loop-owned.** `AsyncDatabaseManager.get_pool()` recreates a pool when a caller arrives on a different loop. If the former owner loop is already closed, stale-pool termination and cleanup discard that pool without trying to schedule callbacks onto the dead loop; unrelated runtime errors still raise. Pinned by `tests/level1/test_pool_loop_recreation.py`.
34
34
  - **Exception diagnostics never read source files.** `ORMException` captures compact caller metadata by walking live frames; `traceback.extract_stack()` is forbidden because its `linecache` filesystem checks can stall an async server. Contract: [`matrx_orm/FEATURE.md`](matrx_orm/FEATURE.md).
35
35
  - **A zero-connection pool is dead, not busy.** After bounded SQL-safe acquire retries, `AsyncDatabaseManager.get_connection()` atomically replaces a pool whose physical size is still zero, then tries once against the replacement. It never replaces a busy `size > 0` pool. Contract: [`matrx_orm/core/FEATURE.md`](matrx_orm/core/FEATURE.md).
@@ -51,9 +51,9 @@ Every table that becomes a Model needs row identity: a single-column primary key
51
51
  - **Signals**: `pre_create`, `post_create`, `pre_save`, `post_save`, `pre_delete`, `post_delete`.
52
52
  - **Schema builder** (`matrx_orm.schema_builder`): code generation for Python + TypeScript type definitions from the DB schema. Postgres **VIEWs** are emitted as **read-only `Model` classes** into the same schema models file (on by default; per-entry `output.include_views: false` opts a schema out, `output.exclude_views: [...]` skips individual views). View models get no manager/DTO/base-class/auto_config (views are not writable), synthesize `_primary_keys` from an `id` column (else the first column, for cache-key/`load_by_id` only), and emit plain typed fields with **no `ForeignKey`** (views carry no FK metadata — nothing cross-schema to resolve). Never hand-write a view model again.
53
53
  - **Secrets battery** (`matrx_orm.secrets_battery`): the ONE canonical user-credential vault — `users.user_secrets` CRUD + the platform's single Fernet primitive, keyed by `CREDENTIALS_ENCRYPTION_KEY`. Any package: `configure_secrets(db_config_name=...)` once, then `await require_user_secret(user_id, key)`. No package ever calls another service to resolve a credential. Contract: [`matrx_orm/secrets_battery/FEATURE.md`](matrx_orm/secrets_battery/FEATURE.md).
54
- - **Polymorphic associations** (`matrx_orm.associations`): `EntityRegistry` (token→Model) + `Associations` — a registry-driven, **access-gated** edge engine over a host's polymorphic edge table. Every `source→target` hop is org-scoped *and* each resolved endpoint is re-authorized against the ACTOR (fail-closed, screams on a genuine leak), so a hop can't widen access past what the actor may reach. 🚨 **The gate is `access_check` (wire `matrx_orm.platform_access_check` → `iam.has_access_for`), NEVER an org-column comparison** — a person belongs to several orgs on purpose, so org-equality denies her own data across her own workspaces (2026-08-30 incident) while granting any co-member of a private row. The edge's `org_id` is a TENANCY stamp, not a permission. Org-equality survives only for UNATTENDED writes (no actor) as an integrity guard, and **system vocabulary is exempt entirely** (existence still required): a model generated with `_rls_variant="system"` is platform-shared dictionary data (seo.keyword/topic) every tenant legitimately links to. Hardcodes no table names — the host wires its own edge + token-registry models. A token whose rows have no Model (a record store's Records) resolves to an **entity door** (`is_entity_door`: `load_by_ids(ids, *, actor_id)`), read as the actor — never by a service identity.
54
+ - **Polymorphic associations** (`matrx_orm.associations`): `EntityRegistry` (token→Model) + `Associations` — a registry-driven, **access-gated** edge engine over a host's polymorphic edge table. Every `source→target` hop is org-scoped *and* each resolved endpoint is re-authorized against the ACTOR (fail-closed, screams on a genuine leak), so a hop can't widen access past what the actor may reach. 🚨 **The gate is `access_check` (wire `matrx_orm.platform_access_check` → `iam.has_access_for`), NEVER an org-column comparison** — a person belongs to several orgs on purpose, so org-equality denies her own data across her own workspaces while granting any co-member of a private row. The edge's `org_id` is a TENANCY stamp, not a permission. Org-equality survives only for UNATTENDED writes (no actor) as an integrity guard, and **system vocabulary is exempt entirely** (existence still required): a model generated with `_rls_variant="system"` is platform-shared dictionary data (seo.keyword/topic) every tenant legitimately links to. Hardcodes no table names — the host wires its own edge + token-registry models. A token whose rows have no Model (a record store's Records) resolves to an **entity door** (`is_entity_door`: `load_by_ids(ids, *, actor_id)`), read as the actor — never by a service identity.
55
55
  - **The host's declared session context** (`matrx_orm.core.session_context`): `configure_session_context(provider)` once at startup, and every **top-level** `transaction()` — including the one `rls_session` opens, before it drops to the restricted role — emits whatever the provider returns as transaction-local GUCs. This is how an application-level declaration reaches Postgres at all: a GUC set outside a transaction has no meaning, so the BEGIN is the only place it can land. aidream wires it to its provenance declaration (`app.actor_tier` / `app.actor_system`, read by `platform.actor_tier()`, the history capture and the tier rule). A nested entry is a savepoint inside a transaction that already carries them and emits nothing. A standalone write (`execute_write` outside any transaction) made while the provider has something to say opens its own transaction for that one statement — and still goes through the write queue, inside a copy of the caller's context, so the queue's concurrency gate is kept and the drain task's foreign context cannot hide the declaration (`tests/level1/test_standalone_write_session_context.py`). No provider configured ⇒ no GUCs (standalone behavior unchanged); a provider that raises fails the transaction loudly rather than committing rows with a false provenance.
56
- - **The platform access policy** (`matrx_orm.platform_access`): `platform_access_check(actor, token, id, level)` → `iam.has_access_for`. The ONE answer to "may this actor touch this row?", shared with RLS and the file-access gate. Fails closed. Never re-derive it from `organization_id` or `created_by` — both are strict subsets that deny legitimate access.
56
+ - **The platform access policy** (`matrx_orm.platform_access`): `platform_access_check(actor, token, id, level)` → `iam.has_access_for`. The ONE answer to "may this actor touch this row?", shared with RLS and the file-access gate. Fails closed by default; `raise_on_error=True` keeps genuine denial false but raises `PlatformAccessUnavailableError` when the policy cannot be called. Never re-derive it from `organization_id` or `created_by` — both are strict subsets that deny legitimate access.
57
57
 
58
58
  Public API is defined explicitly in `matrx_orm/__init__.py` (100+ exports). Treat changes there as semver-breaking.
59
59
 
@@ -126,9 +126,7 @@ command is `uv run python db/generate.py --schema <schema> --models-only`.
126
126
  is the entire `from db.models import X` surface; it is always written from the FULL,
127
127
  unfiltered `generate[]` list, never from the `only_schemas` subset — and model names for
128
128
  schemas the run did not regenerate are read back from their modules on disk (AST, no
129
- import) so cross-schema collision de-flattening stays complete. Writing it from a subset
130
- once truncated it to one schema and broke imports repo-wide, silently at generation time
131
- (hand-repaired in 79505fee8). `_write_host_models_aggregator` now RAISES
129
+ import) so cross-schema collision de-flattening stays complete. `_write_host_models_aggregator` now RAISES
132
130
  `AggregatorShrinkError` naming every schema that would be lost, and writes nothing.
133
131
  Guard: `tests/schema_builder/test_host_models_aggregator.py`.
134
132
 
@@ -162,22 +160,13 @@ enforced in code:
162
160
  as stale (`ModelState.sweep_stale`, every `STALE_SWEEP_EVERY_WRITES`); an
163
161
  append-only writer of big JSON passes `returning=[...]` to `upsert` /
164
162
  `bulk_create` (or uses `insert_returning`) so its rows are neither read back
165
- nor cached. Before this an unread entry lived for the whole process, and a
166
- workflow worker's checkpoints/outcomes/events grew it until ECS killed the
167
- task for memory (MKT-111, 2026-10-15). The sweep pops oldest-first and stops at
163
+ nor cached. The sweep pops oldest-first and stops at
168
164
  the first fresh entry, and `cache_bulk` yields the loop once per sweep window, so
169
- caching a big read never stalls the server (a full-scan sweep froze it 2-6 s on a
170
- 21k-row read, 2026-10-17). Guards: `tests/test_state_cache_sweeps_stale.py`,
165
+ caching a big read never stalls the server. Guards: `tests/test_state_cache_sweeps_stale.py`,
171
166
  `tests/test_a_bulk_cache_never_stalls_the_loop.py`.
172
167
 
173
- This is not theoretical hygiene. On 2026-07-25 rule 1 was missing: `session/flush.py`
174
- builds a `{pk + payload}` stub per queued UPDATE and `bulk_update` cached it, so a
175
- chat conversation read back with `organization_id = None` right after its own
176
- turn-1 title/`message_count` UPDATE flushed — and aidream stopped the user's next
177
- turn on a value Postgres had held correctly the entire time. Guards:
178
- `tests/session/test_cache_no_downgrade.py`.
168
+ The no-downgrade guard is `tests/session/test_cache_no_downgrade.py`.
179
169
 
180
170
  ## Known issues
181
171
 
182
- - **No CLAUDE.md existed before this commit** — that was the main gap.
183
- - Otherwise this package is the reference-quality standalone package in the monorepo. See root `PACKAGES_MIGRATION_PLAN.md` if any new gaps emerge.
172
+ Package migration gaps are tracked in the root `PACKAGES_MIGRATION_PLAN.md`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.255
3
+ Version: 3.1.256
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
@@ -49,6 +49,7 @@ from .platform_access import (
49
49
  PermissionLevel,
50
50
  configure_platform_access,
51
51
  permission_level_rank,
52
+ PlatformAccessUnavailableError,
52
53
  platform_access_check,
53
54
  platform_accessible_ids,
54
55
  platform_edge_requirement,
@@ -439,6 +440,7 @@ __all__ = [
439
440
  "register_database",
440
441
  "register_database_from_env",
441
442
  "register_platform_db",
443
+ "PlatformAccessUnavailableError",
442
444
  "platform_access_check",
443
445
  "platform_accessible_ids",
444
446
  "configure_platform_access",
@@ -169,11 +169,17 @@ def _resolve_database() -> str:
169
169
  raise
170
170
 
171
171
 
172
+ class PlatformAccessUnavailableError(RuntimeError):
173
+ """The canonical policy could not be evaluated."""
174
+
175
+
172
176
  async def platform_access_check(
173
177
  actor_id: str,
174
178
  entity_token: str,
175
179
  entity_id: str,
176
180
  required_level: str = "viewer",
181
+ *,
182
+ raise_on_error: bool = False,
177
183
  ) -> bool:
178
184
  """Return whether ``actor_id`` may reach ``entity_token:entity_id``.
179
185
 
@@ -190,7 +196,8 @@ async def platform_access_check(
190
196
  raise escapes them as a 500 on an unrelated code path. The FIRST failure
191
197
  screams (a wiring bug must never be quiet), and
192
198
  :func:`verify_platform_access_wiring` exists so startup catches it before a
193
- user does.
199
+ user does. ``raise_on_error=True`` preserves a policy False but raises a fixed
200
+ unavailable error when the policy cannot be evaluated.
194
201
  """
195
202
  if not actor_id or not entity_token or not entity_id:
196
203
  return False
@@ -227,6 +234,8 @@ async def platform_access_check(
227
234
  "This message prints once per process.",
228
235
  color="red",
229
236
  )
237
+ if raise_on_error:
238
+ raise PlatformAccessUnavailableError("Access policy is temporarily unavailable.") from None
230
239
  return False
231
240
 
232
241
 
@@ -15,6 +15,16 @@ request in the process.
15
15
 
16
16
  `compile()` remains deliberately synchronous for synchronous callers.
17
17
 
18
+ ## Row-lock strength
19
+
20
+ `Model.filter(...).select_for_update()` retains `FOR UPDATE` by default;
21
+ `select_for_update(mode="SHARE")` emits `FOR SHARE` through the same query and
22
+ terminal paths. The keyword-only mode accepts only `UPDATE` or `SHARE`;
23
+ `of`, `nowait`, and `skip_locked` apply to either mode. Use row locks inside
24
+ the transaction that consumes the protected rows. SQL compilation checks are
25
+ in `tests/level1/test_row_lock_modes.py`; they do not establish database lock
26
+ compatibility or transaction lifetime.
27
+
18
28
  ## Large scans and the identity cache
19
29
 
20
30
  `.all()` populates `StateManager` by default so ordinary repeated identity reads
@@ -85,20 +95,15 @@ resource authorization; ordinary `get()` remains read-your-own-writes.
85
95
  `count()`, `update()` and `delete()` rebuild SQL from the WHERE tail that
86
96
  `_extract_where_clause` peels off the built SELECT. That peel drops the
87
97
  SELECT's trailing clauses, so `_prune_param_refs` drops the binds that belonged
88
- to them and renumbers the survivors `$1..$k`. Without it,
89
- `Model.filter().limit(1).count()` shipped `SELECT COUNT(*) …` (no placeholders)
90
- with LIMIT's bind still attached and asyncpg raised *the server expects 0
91
- arguments for this query, 1 was passed* — surfaced as `CacheError`, and it
92
- hard-500'd `GET /rag/admin/overview` until 2026-09-12. Invariant:
98
+ to them and renumbers the survivors `$1..$k`. Invariant:
93
99
  `_max_param_ref(sql) == len(params)` for every emitted statement
94
100
  (`tests/level1/test_count_limit_param_leak.py`).
95
101
 
96
102
  ## One predicate family per expression
97
103
 
98
104
  `Q` composes with `Q`; a typed `Predicate` composes with a `Predicate`. Mixing
99
- them (`Q(a=1) | Is(F("x"), None)`) raises `TypeError` naming the remedy — it
100
- used to build a Q holding a Predicate child and die later with `'Is' object has
101
- no attribute 'kwargs'`. `Filter` accepts direct aggregate calls only:
105
+ them (`Q(a=1) | Is(F("x"), None)`) raises `TypeError` naming the remedy.
106
+ `Filter` accepts direct aggregate calls only:
102
107
  `Aggregate` subclasses and the explicit two-expression `JsonbObjectAgg`.
103
108
  `JsonbObjectAgg` is accepted because PostgreSQL permits
104
109
  `jsonb_object_agg(...) FILTER (WHERE ...)`, even though it cannot inherit the
@@ -116,31 +121,10 @@ rewrites the next `update()` to `WHERE pk IN (SELECT pk FROM t WHERE <filters>
116
121
  ORDER BY pk FOR UPDATE)`. Use it on any multi-row UPDATE that concurrent callers
117
122
  can run over an OVERLAPPING row set.
118
123
 
119
- Sorting the id array the caller passed does NOT fix this — the PLAN decides lock
120
- order, not the array. `= ANY($1)` may become a seq scan, which locks in heap
121
- order, and heap order MOVES because these very updates rewrite the rows they
122
- scan. Two copies of the same statement then take the same locks in opposite
123
- orders and Postgres kills one (40P01): four parallel `ai.agent.start`
124
- invocations stamping `last_used_at` on one org's ~185 `users.user_secrets` rows
125
- errored a live workflow run on 2026-09-12. Ordering inside a `FOR UPDATE`
126
- sub-select is the only form that pins acquisition order (`LockRows` sits above
127
- the `Sort`), and it stays ONE statement — a 185-row telemetry write does not
128
- become 185 round trips. Requires a single-column primary key and an unaliased
129
- target; both refuse loudly. A non-SQL adapter cannot express it and says so
130
- rather than dropping the guard silently. Guard:
131
- `tests/level1/test_deadlock_retry.py`.
132
-
133
- ## Change log
134
-
135
- - 2026-09-12 — Added `lock_rows_in_pk_order()`: a multi-row UPDATE can take its
136
- row locks ascending by primary key instead of in plan-scan order.
137
- - 2026-09-12 — Rebuilt statements prune unreferenced binds; Q/Predicate mixing
138
- and invalid wrapped `Filter` targets are refused at construction, while
139
- direct `JsonbObjectAgg` FILTER expressions remain valid.
140
- - 2026-09-10 — Projection follows caller order: `select`/`values` no longer
141
- reversed, `only()` no longer hash-ordered.
142
- - 2026-08-31 — Kept tuple-valued collection lookups as one array bind parameter.
143
- - 2026-08-28 — Added persisted-state counts that bypass pending-row hydration.
144
- - 2026-08-18 — Moved async terminal SQL compilation off the event loop.
145
- - 2026-08-15 — Moved bulk hydration off the event loop, made complete-primary-key
146
- misses constant-time, and documented cache bypass for request-local scans.
124
+ Sorting the caller's id array does not control lock order: the query plan does.
125
+ A sequential scan locks in heap order, which updates can change. Concurrent
126
+ statements can acquire overlapping locks in opposite orders and deadlock
127
+ (40P01). Ordering inside a `FOR UPDATE` sub-select pins acquisition order
128
+ (`LockRows` sits above `Sort`) in one statement. Requires a single-column
129
+ primary key and an unaliased target; unsupported targets and adapters fail
130
+ explicitly. Guard: `tests/level1/test_deadlock_retry.py`.
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import re
5
- from typing import TYPE_CHECKING, Any, Generic, TypeVar
5
+ from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar
6
6
 
7
7
  from matrx_utils import vcprint
8
8
 
@@ -550,13 +550,22 @@ class QueryBuilder(Generic[ModelT]):
550
550
  nowait: bool = False,
551
551
  skip_locked: bool = False,
552
552
  of: list[str] | None = None,
553
+ *,
554
+ mode: Literal["UPDATE", "SHARE"] = "UPDATE",
553
555
  ) -> QueryBuilder[ModelT]:
554
- """Append ``FOR UPDATE [NOWAIT | SKIP LOCKED] [OF ...]`` to the query.
556
+ """Append ``FOR UPDATE|SHARE [OF ...] [NOWAIT | SKIP LOCKED]`` to the query.
555
557
 
556
558
  Must be used inside a transaction.
557
559
  """
560
+ if mode not in ("UPDATE", "SHARE"):
561
+ raise ValueError("Row-lock mode must be UPDATE or SHARE")
558
562
  new = self._clone()
559
- new.for_update_opts = {"nowait": nowait, "skip_locked": skip_locked, "of": of or []}
563
+ new.for_update_opts = {
564
+ "nowait": nowait,
565
+ "skip_locked": skip_locked,
566
+ "of": of or [],
567
+ "mode": mode,
568
+ }
560
569
  return new
561
570
 
562
571
  def lock_rows_in_pk_order(self) -> QueryBuilder[ModelT]:
@@ -51,6 +51,7 @@ _TOP_LEVEL_WHERE_STOPPERS = (
51
51
  "LIMIT",
52
52
  "OFFSET",
53
53
  "FOR UPDATE",
54
+ "FOR SHARE",
54
55
  )
55
56
 
56
57
 
@@ -937,19 +938,22 @@ class QueryExecutor:
937
938
  sql += f" OFFSET ${len(params) + 1}"
938
939
  params.append(self._full_query_dict["offset"])
939
940
 
940
- # -- FOR UPDATE -------------------------------------------------
941
+ # -- Row locks --------------------------------------------------
941
942
  for_update = self._full_query_dict.get("for_update")
942
943
  if for_update:
943
- clause = " FOR UPDATE"
944
- if for_update.get("nowait"):
945
- clause += " NOWAIT"
946
- elif for_update.get("skip_locked"):
947
- clause += " SKIP LOCKED"
944
+ mode = for_update.get("mode", "UPDATE")
945
+ if mode not in ("UPDATE", "SHARE"):
946
+ raise ValueError("Row-lock mode must be UPDATE or SHARE")
947
+ clause = " FOR " + mode
948
948
  of_tables = for_update.get("of", [])
949
949
  if of_tables:
950
950
  from matrx_orm.core.expressions import F
951
951
 
952
952
  clause += " OF " + ", ".join(F(table).field_name for table in of_tables)
953
+ if for_update.get("nowait"):
954
+ clause += " NOWAIT"
955
+ elif for_update.get("skip_locked"):
956
+ clause += " SKIP LOCKED"
953
957
  sql += clause
954
958
 
955
959
  if debug:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.1.255"
3
+ version = "3.1.256"
4
4
  description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -0,0 +1,138 @@
1
+ """The public model query must carry its chosen lock mode to the SQL adapter.
2
+
3
+ Only the external database is replaced. SQL compilation, cloning, async terminals
4
+ and model hydration remain real; literal SQL is the independent oracle.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+
11
+ import pytest
12
+ from matrx_orm.adapters import AdapterRegistry
13
+ from matrx_orm.core.base import Model, ModelMeta
14
+ from matrx_orm.core.fields import IntegerField, TextField
15
+ from matrx_orm.query.executor import QueryExecutor
16
+
17
+
18
+ class _CapturingDatabase:
19
+ def __init__(self) -> None:
20
+ self.calls: list[tuple[str, tuple[Any, ...]]] = []
21
+
22
+ async def execute_query_dict(self, query: dict[str, Any]) -> Any:
23
+ return NotImplemented
24
+
25
+ async def execute_query(self, sql: str, *params: Any) -> list[dict[str, Any]]:
26
+ self.calls.append((sql, params))
27
+ if sql.startswith("SELECT COUNT(*)"):
28
+ return [{"count": 2}]
29
+ return [{"id": 17, "name": "Harbor Dental"}]
30
+
31
+
32
+ @pytest.fixture
33
+ def query_surface(monkeypatch: pytest.MonkeyPatch) -> tuple[type[Model], _CapturingDatabase]:
34
+ model = ModelMeta("RowLockPractice", (Model,), {
35
+ "id": IntegerField(primary_key=True),
36
+ "name": TextField(),
37
+ "_database": "row_lock_offline",
38
+ "_table_name": "practice",
39
+ })
40
+ database = _CapturingDatabase()
41
+ monkeypatch.setattr(AdapterRegistry, "get", classmethod(lambda cls, name: database))
42
+ return model, database
43
+
44
+
45
+ @pytest.mark.parametrize(("options", "suffix"), [
46
+ ({}, "FOR UPDATE"),
47
+ ({"mode": "UPDATE"}, "FOR UPDATE"),
48
+ ({"mode": "SHARE"}, "FOR SHARE"),
49
+ ({"mode": "SHARE", "nowait": True}, "FOR SHARE NOWAIT"),
50
+ ({"mode": "UPDATE", "skip_locked": True}, "FOR UPDATE SKIP LOCKED"),
51
+ ({"mode": "SHARE", "skip_locked": True}, "FOR SHARE SKIP LOCKED"),
52
+ ({"mode": "UPDATE", "of": ["practice"], "nowait": True}, "FOR UPDATE OF practice NOWAIT"),
53
+ ({"mode": "SHARE", "of": ["practice"], "nowait": True}, "FOR SHARE OF practice NOWAIT"),
54
+ ({"mode": "SHARE", "of": ["practice"], "skip_locked": True}, "FOR SHARE OF practice SKIP LOCKED"),
55
+ # Existing precedence is retained when both wait options are supplied.
56
+ ({"mode": "SHARE", "nowait": True, "skip_locked": True}, "FOR SHARE NOWAIT"),
57
+ ])
58
+ def test_compile_preserves_selected_mode_and_wait_options(query_surface, options, suffix):
59
+ model, _ = query_surface
60
+ query = model.filter(id=17).select_for_update(**options).order_by("id").limit(2)
61
+ assert query.compile("id") == (
62
+ "SELECT id FROM public.practice WHERE id = $1 ORDER BY id ASC LIMIT $2 " + suffix,
63
+ [17, 2],
64
+ )
65
+
66
+
67
+ def test_existing_positional_lock_options_still_compile(query_surface):
68
+ model, _ = query_surface
69
+ assert model.filter().select_for_update(True, False, ["practice"]).compile() == (
70
+ "SELECT * FROM public.practice FOR UPDATE OF practice NOWAIT", [],
71
+ )
72
+
73
+
74
+ def test_lock_branches_do_not_change_the_base_or_each_other(query_surface):
75
+ model, _ = query_surface
76
+ base = model.filter(id=17)
77
+ shared = base.select_for_update(mode="SHARE")
78
+ updated = shared.select_for_update().order_by("id")
79
+ assert base.compile() == ("SELECT * FROM public.practice WHERE id = $1", [17])
80
+ assert shared.compile() == ("SELECT * FROM public.practice WHERE id = $1 FOR SHARE", [17])
81
+ assert updated.compile() == (
82
+ "SELECT * FROM public.practice WHERE id = $1 ORDER BY id ASC FOR UPDATE", [17],
83
+ )
84
+
85
+
86
+ @pytest.mark.parametrize("mode", ["SHARE", "UPDATE"])
87
+ @pytest.mark.parametrize("terminal", ["values", "all"])
88
+ async def test_async_terminal_sends_lock_to_database(query_surface, mode, terminal):
89
+ model, database = query_surface
90
+ query = model.filter(id=17).select_for_update(mode=mode).order_by("id")
91
+ if terminal == "values":
92
+ assert await query.values("id", "name") == [{"id": 17, "name": "Harbor Dental"}]
93
+ projection = "id, name"
94
+ else:
95
+ rows = await query.all(use_cache=False)
96
+ assert [(row.id, row.name) for row in rows] == [(17, "Harbor Dental")]
97
+ projection = "*"
98
+ assert database.calls == [(
99
+ f"SELECT {projection} FROM public.practice WHERE id = $1 ORDER BY id ASC FOR {mode}",
100
+ (17,),
101
+ )]
102
+
103
+
104
+ @pytest.mark.parametrize("mode", ["SHARE", "UPDATE"])
105
+ async def test_count_rebuild_drops_lock_suffix_without_leaking_params(query_surface, mode):
106
+ model, database = query_surface
107
+ result = await model.filter(id=17).select_for_update(mode=mode).count()
108
+ assert result == 2
109
+ assert database.calls == [("SELECT COUNT(*) as count FROM public.practice WHERE id = $1", (17,))]
110
+
111
+
112
+ @pytest.mark.parametrize("mode", ["share", "NO KEY UPDATE", "SHARE; SELECT 1", "", None, 1])
113
+ def test_builder_refuses_unrecognized_lock_mode(query_surface, mode):
114
+ model, _ = query_surface
115
+ with pytest.raises(ValueError, match="Row-lock mode must be UPDATE or SHARE"):
116
+ model.filter().select_for_update(mode=mode)
117
+
118
+
119
+ def test_executor_refuses_mode_injected_through_query_dictionary(query_surface):
120
+ model, database = query_surface
121
+ query = model.filter().select_for_update()._build_query()
122
+ query["for_update"]["mode"] = "SHARE; SELECT 1"
123
+ with pytest.raises(ValueError, match="Row-lock mode must be UPDATE or SHARE"):
124
+ QueryExecutor(query)
125
+ assert database.calls == []
126
+
127
+
128
+ def test_legacy_query_dictionary_defaults_to_update(query_surface):
129
+ model, _ = query_surface
130
+ query = model.filter()._build_query()
131
+ query["for_update"] = {"nowait": True}
132
+ assert QueryExecutor(query).query == "SELECT * FROM public.practice FOR UPDATE NOWAIT"
133
+
134
+
135
+ def test_lock_table_identifier_is_validated(query_surface):
136
+ model, _ = query_surface
137
+ with pytest.raises(ValueError, match="Unsafe field reference"):
138
+ model.filter().select_for_update(mode="SHARE", of=["practice; SELECT 1"]).compile()
@@ -0,0 +1,38 @@
1
+ """The strict opt-in distinguishes policy denial from unavailable authority."""
2
+
3
+ import asyncio
4
+ from unittest.mock import AsyncMock
5
+
6
+ import pytest
7
+ from matrx_orm import PlatformAccessUnavailableError
8
+ from matrx_orm import platform_access as access
9
+ from matrx_orm.core import db_function
10
+
11
+
12
+ @pytest.mark.parametrize("strict", [False, True])
13
+ async def test_real_policy_false_is_denial(monkeypatch, strict):
14
+ monkeypatch.setattr(access, "_resolve_database", lambda: "test")
15
+ call = AsyncMock(return_value=False)
16
+ monkeypatch.setattr(db_function, "call_function", call)
17
+ assert not await access.platform_access_check("actor", "record", "id", raise_on_error=strict)
18
+ assert call.await_count == 1
19
+
20
+
21
+ async def test_actual_invocation_failure_raises_only_for_strict(monkeypatch):
22
+ monkeypatch.setattr(access, "_resolve_database", lambda: "test")
23
+ monkeypatch.setattr(access, "_policy_failure_announced", True)
24
+ call = AsyncMock(side_effect=ConnectionError("private transport diagnostic"))
25
+ monkeypatch.setattr(db_function, "call_function", call)
26
+ assert not await access.platform_access_check("actor", "record", "id")
27
+ with pytest.raises(
28
+ PlatformAccessUnavailableError, match="^Access policy is temporarily unavailable.$"
29
+ ):
30
+ await access.platform_access_check("actor", "record", "id", raise_on_error=True)
31
+ assert call.await_count == 2
32
+
33
+
34
+ async def test_cancel_propagates(monkeypatch):
35
+ monkeypatch.setattr(access, "_resolve_database", lambda: "test")
36
+ monkeypatch.setattr(db_function, "call_function", AsyncMock(side_effect=asyncio.CancelledError))
37
+ with pytest.raises(asyncio.CancelledError):
38
+ await access.platform_access_check("actor", "record", "id", raise_on_error=True)
File without changes
File without changes
File without changes
File without changes
File without changes