matrx-orm 3.1.233__tar.gz → 3.1.235__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 (444) hide show
  1. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/.gitignore +6 -0
  2. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/PKG-INFO +2 -2
  3. matrx_orm-3.1.235/matrx_orm/core/foreign_session_state.py +194 -0
  4. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/pytest_plugin.py +22 -0
  5. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/query/builder.py +15 -1
  6. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/fallback.py +34 -0
  7. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/pyproject.toml +2 -2
  8. matrx_orm-3.1.235/tests/level1/test_borrowed_role_never_reaches_server_calls.py +279 -0
  9. matrx_orm-3.1.235/tests/session/test_tests_never_write_live_system_error.py +37 -0
  10. matrx_orm-3.1.235/tests/test_first_asks_the_database_for_one_row.py +48 -0
  11. matrx_orm-3.1.233/matrx_orm/core/foreign_session_state.py +0 -112
  12. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/AGENTS.md +0 -0
  13. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/BASE_CLASS_METHODS.md +0 -0
  14. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/CHANGELOG.md +0 -0
  15. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/CLAUDE.md +0 -0
  16. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/MODEL_API.md +0 -0
  17. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/README.md +0 -0
  18. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/database/orm/extended/managers/ai_model_base.py +0 -0
  19. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/docs/migrations.md +0 -0
  20. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/FEATURE.md +0 -0
  21. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/MODULE_README.md +0 -0
  22. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/__init__.py +0 -0
  23. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/adapters/MODULE_README.md +0 -0
  24. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/adapters/__init__.py +0 -0
  25. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/adapters/async_postgresql.py +0 -0
  26. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/adapters/base_adapter.py +0 -0
  27. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  28. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/adapters/supabase_adapter.py +0 -0
  29. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/admin/FEATURE.md +0 -0
  30. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/admin/__init__.py +0 -0
  31. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/admin/router.py +0 -0
  32. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/api/__init__.py +0 -0
  33. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/api/auth.py +0 -0
  34. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/api/config.py +0 -0
  35. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/api/handlers.py +0 -0
  36. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/api/protocol.py +0 -0
  37. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/api/server.py +0 -0
  38. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/associations.py +0 -0
  39. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/cache_debug.py +0 -0
  40. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/catalog.py +0 -0
  41. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/catalog_sql.py +0 -0
  42. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/client/MODULE_README.md +0 -0
  43. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/client/__init__.py +0 -0
  44. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/client/postgres_connection.py +0 -0
  45. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/client/postgrest.py +0 -0
  46. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/client/supabase_auth.py +0 -0
  47. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/client/supabase_config.py +0 -0
  48. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/client/supabase_manager.py +0 -0
  49. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  50. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/FEATURE.md +0 -0
  51. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/MODULE_README.md +0 -0
  52. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  53. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/TASKS.md +0 -0
  54. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/__init__.py +0 -0
  55. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/admin_lane.py +0 -0
  56. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/async_db_manager.py +0 -0
  57. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/base.py +0 -0
  58. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/config.py +0 -0
  59. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/connections.py +0 -0
  60. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/db_function.py +0 -0
  61. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/diagnostics.py +0 -0
  62. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/expressions.py +0 -0
  63. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/extended.py +0 -0
  64. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/fields.py +0 -0
  65. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/introspection.py +0 -0
  66. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/listen.py +0 -0
  67. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/loop_filters.py +0 -0
  68. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/model_dto.py +0 -0
  69. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/model_view.py +0 -0
  70. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/one_database.py +0 -0
  71. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/paginator.py +0 -0
  72. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/pool_maintenance.py +0 -0
  73. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/pool_watch.py +0 -0
  74. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/pooler_role.py +0 -0
  75. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/pydantic_bridge.py +0 -0
  76. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/read_write.py +0 -0
  77. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/registry.py +0 -0
  78. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/relations.py +0 -0
  79. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/resilience.py +0 -0
  80. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/rls_session.py +0 -0
  81. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/session_context.py +0 -0
  82. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/signals.py +0 -0
  83. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/statement_ceiling.py +0 -0
  84. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/tls.py +0 -0
  85. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/transaction.py +0 -0
  86. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/types.py +0 -0
  87. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/write_queue.py +0 -0
  88. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/core/write_retry.py +0 -0
  89. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/entity.py +0 -0
  90. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/error_handling.py +0 -0
  91. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/exceptions.py +0 -0
  92. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/extended/__init__.py +0 -0
  93. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/extended/app_error_handler.py +0 -0
  94. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/identity_rule.py +0 -0
  95. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/local_sqlite.py +0 -0
  96. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/middleware/__init__.py +0 -0
  97. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/middleware/base.py +0 -0
  98. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/MODULE_README.md +0 -0
  99. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/__init__.py +0 -0
  100. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/cli.py +0 -0
  101. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/ddl.py +0 -0
  102. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/diff.py +0 -0
  103. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/executor.py +0 -0
  104. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/integration.py +0 -0
  105. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/loader.py +0 -0
  106. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/operations.py +0 -0
  107. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/state.py +0 -0
  108. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/migrations/table_filter.py +0 -0
  109. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/no_org_assignment.py +0 -0
  110. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/FEATURE.md +0 -0
  111. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/MODULE_README.md +0 -0
  112. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/__init__.py +0 -0
  113. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/bulk_update_values.py +0 -0
  114. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/catalog_select.py +0 -0
  115. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/conflict.py +0 -0
  116. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/conflict_writes.py +0 -0
  117. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/create.py +0 -0
  118. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/db_functions.py +0 -0
  119. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/delete.py +0 -0
  120. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/dynamic_admin.py +0 -0
  121. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/dynamic_crud.py +0 -0
  122. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/graph_walk.py +0 -0
  123. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/latest_rows.py +0 -0
  124. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/queue_claim.py +0 -0
  125. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/read.py +0 -0
  126. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/relational_backfill.py +0 -0
  127. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/staging_load.py +0 -0
  128. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/operations/update.py +0 -0
  129. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/platform_access.py +0 -0
  130. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/platform_db.py +0 -0
  131. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/py.typed +0 -0
  132. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/pytest_lock_guard.py +0 -0
  133. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/python_sql/MODULE_README.md +0 -0
  134. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/python_sql/__init__.py +0 -0
  135. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/python_sql/db_objects.py +0 -0
  136. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  137. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  138. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/query/FEATURE.md +0 -0
  139. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/query/MODULE_README.md +0 -0
  140. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/query/__init__.py +0 -0
  141. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/query/executor.py +0 -0
  142. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/resources/__init__.py +0 -0
  143. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
  144. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
  145. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/FEATURE.md +0 -0
  146. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  147. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/__init__.py +0 -0
  148. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/code_handler.py +0 -0
  149. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  150. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/columns.py +0 -0
  151. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/common.py +0 -0
  152. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/diff_preview.py +0 -0
  153. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  154. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/excluded_targets.py +0 -0
  155. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/generator.py +0 -0
  156. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  157. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  158. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  159. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  160. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/package_wiring.py +0 -0
  161. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/relationships.py +0 -0
  162. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/runner.py +0 -0
  163. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/schema.py +0 -0
  164. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/schema_manager.py +0 -0
  165. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/tables.py +0 -0
  166. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/schema_builder/views.py +0 -0
  167. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/FEATURE.md +0 -0
  168. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/__init__.py +0 -0
  169. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/attachments.py +0 -0
  170. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/backup_restore.py +0 -0
  171. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/crypto.py +0 -0
  172. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  173. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/items.py +0 -0
  174. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/model.py +0 -0
  175. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
  176. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/native_enable.py +0 -0
  177. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
  178. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
  179. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/native_source.py +0 -0
  180. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/org_service.py +0 -0
  181. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/org_vault_gate.py +0 -0
  182. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/service.py +0 -0
  183. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/secrets_battery/vault_history.py +0 -0
  184. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/README.md +0 -0
  185. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/__init__.py +0 -0
  186. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/coalesce.py +0 -0
  187. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/dag.py +0 -0
  188. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/errors.py +0 -0
  189. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/flush.py +0 -0
  190. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/lifecycle.py +0 -0
  191. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/managed.py +0 -0
  192. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/op.py +0 -0
  193. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/reads.py +0 -0
  194. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/session.py +0 -0
  195. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/session/telemetry.py +0 -0
  196. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  197. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/sql_executor/__init__.py +0 -0
  198. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/sql_executor/executor.py +0 -0
  199. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/sql_executor/queries.py +0 -0
  200. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/sql_executor/registry.py +0 -0
  201. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/sql_executor/types.py +0 -0
  202. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/sql_executor/utils.py +0 -0
  203. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/state.py +0 -0
  204. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/utils/__init__.py +0 -0
  205. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/utils/sql_utils.py +0 -0
  206. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/utils/type_converters.py +0 -0
  207. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/matrx_orm/write_actor.py +0 -0
  208. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/scripts/git-branches.sh +0 -0
  209. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/scripts/publish.sh +0 -0
  210. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/scripts/release.sh +0 -0
  211. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/MODULE_README.md +0 -0
  212. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/__init__.py +0 -0
  213. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/conftest.py +0 -0
  214. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/MODULE_README.md +0 -0
  215. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/__init__.py +0 -0
  216. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_adapter_registry.py +0 -0
  217. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_admin_db_columns.py +0 -0
  218. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_admin_response_rendering.py +0 -0
  219. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_admin_search_negation.py +0 -0
  220. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_admin_search_sentinels.py +0 -0
  221. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_advisory_lock.py +0 -0
  222. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_agent_message.py +0 -0
  223. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_api_auth.py +0 -0
  224. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_api_config.py +0 -0
  225. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_api_handlers.py +0 -0
  226. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_api_protocol.py +0 -0
  227. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_array_agg_order_by.py +0 -0
  228. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_association_trigger_actor_naming.py +0 -0
  229. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_associations.py +0 -0
  230. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_associations_wiring.py +0 -0
  231. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_bulk_hydration_offload.py +0 -0
  232. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_bulk_update_by_pk.py +0 -0
  233. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  234. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_cache_debug.py +0 -0
  235. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_call_function.py +0 -0
  236. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_case_when.py +0 -0
  237. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_catalog.py +0 -0
  238. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_column_scoped_writes.py +0 -0
  239. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_committed_row_guard.py +0 -0
  240. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_composite_pk_filter.py +0 -0
  241. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_composite_pk_write_paths.py +0 -0
  242. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_config.py +0 -0
  243. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_conflict_target.py +0 -0
  244. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_conflict_writes.py +0 -0
  245. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_connection_codecs.py +0 -0
  246. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_connection_poison_guard.py +0 -0
  247. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_connection_primitives.py +0 -0
  248. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_count_composite_distinct.py +0 -0
  249. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_count_limit_param_leak.py +0 -0
  250. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_db_function_field.py +0 -0
  251. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_ddl_generator.py +0 -0
  252. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_deadlock_retry.py +0 -0
  253. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_derived_table.py +0 -0
  254. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_dirty_tracking.py +0 -0
  255. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_dynamic_crud.py +0 -0
  256. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_exceptions.py +0 -0
  257. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  258. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_expression_primitives.py +0 -0
  259. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_fields.py +0 -0
  260. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  261. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_fk_cycle_resolution.py +0 -0
  262. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_from_alias.py +0 -0
  263. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_fts.py +0 -0
  264. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_func_expression_args.py +0 -0
  265. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_graph_walk.py +0 -0
  266. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_having_annotate_alias.py +0 -0
  267. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  268. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_introspect_rls_policies.py +0 -0
  269. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_join_and_aggregate.py +0 -0
  270. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_latest_rows.py +0 -0
  271. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_listen_notify.py +0 -0
  272. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_local_sqlite.py +0 -0
  273. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_lock_contention_retry.py +0 -0
  274. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_managed_write_guard.py +0 -0
  275. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_matrx_entity.py +0 -0
  276. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_migration_diff_types.py +0 -0
  277. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_migration_loader.py +0 -0
  278. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_model_instance.py +0 -0
  279. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_model_meta.py +0 -0
  280. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_optimistic_concurrency.py +0 -0
  281. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_order_by_expression.py +0 -0
  282. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_org_assignment_inventory.py +0 -0
  283. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
  284. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_parameter_name_hydration.py +0 -0
  285. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_patch_jsonb_path.py +0 -0
  286. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_percentile_cont.py +0 -0
  287. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_persisted_count.py +0 -0
  288. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_person_scoped_session_bypasses_shared_cache.py +0 -0
  289. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_pool_create_never_wedges.py +0 -0
  290. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_pool_holder_census.py +0 -0
  291. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_pool_loop_recreation.py +0 -0
  292. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_pool_maintenance.py +0 -0
  293. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_postgrest_filters.py +0 -0
  294. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_pydantic_bridge.py +0 -0
  295. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_query_builder.py +0 -0
  296. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_query_error_detail.py +0 -0
  297. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_query_executor_sql.py +0 -0
  298. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_query_timeout.py +0 -0
  299. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  300. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_queue_claim.py +0 -0
  301. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_read_connection_retry.py +0 -0
  302. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_read_write_declared.py +0 -0
  303. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_registry.py +0 -0
  304. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_registry_multi_database.py +0 -0
  305. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_registry_multi_schema.py +0 -0
  306. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_relational_backfill.py +0 -0
  307. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_relations.py +0 -0
  308. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_resilience.py +0 -0
  309. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_rls_session_scope.py +0 -0
  310. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_schema_exists.py +0 -0
  311. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_session_advisory_lock.py +0 -0
  312. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_standalone_write_session_context.py +0 -0
  313. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_state_cache.py +0 -0
  314. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_subquery_filter_raw.py +0 -0
  315. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_subquery_in_filter.py +0 -0
  316. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_subquery_nested_expressions.py +0 -0
  317. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_supabase_auth.py +0 -0
  318. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_supabase_config.py +0 -0
  319. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_tls.py +0 -0
  320. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_transaction_database_identity.py +0 -0
  321. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_transaction_gucs_one_round_trip.py +0 -0
  322. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_transaction_pool_release_sends_nothing.py +0 -0
  323. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_typed_join_projection.py +0 -0
  324. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_typed_predicates.py +0 -0
  325. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_update_case_expression.py +0 -0
  326. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_update_subquery.py +0 -0
  327. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_update_with_rebase.py +0 -0
  328. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_upsert_default_leak.py +0 -0
  329. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_upsert_with_conflict.py +0 -0
  330. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_values_jsonb_decode.py +0 -0
  331. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_watched_lifecycle.py +0 -0
  332. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_write_queue_deadline.py +0 -0
  333. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level1/test_write_retry_semantics.py +0 -0
  334. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/MODULE_README.md +0 -0
  335. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/__init__.py +0 -0
  336. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/conftest.py +0 -0
  337. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_bulk_ops.py +0 -0
  338. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_cache_integration.py +0 -0
  339. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_crud.py +0 -0
  340. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_foreign_keys.py +0 -0
  341. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_m2m.py +0 -0
  342. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_manager.py +0 -0
  343. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_migrations_live.py +0 -0
  344. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_query_execution.py +0 -0
  345. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/test_schema_diff.py +0 -0
  346. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
  347. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/live_pooler_role.py +0 -0
  348. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/native_source_cases.py +0 -0
  349. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/native_source_oracle.rs +0 -0
  350. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project/.env.example +0 -0
  351. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project/README.md +0 -0
  352. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project/__init__.py +0 -0
  353. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project/generate.py +0 -0
  354. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project/generated/.gitkeep +0 -0
  355. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project/matrx_orm.yaml +0 -0
  356. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project/test_schema_generation.py +0 -0
  357. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project_desktop/.env.example +0 -0
  358. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project_desktop/README.md +0 -0
  359. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project_desktop/__init__.py +0 -0
  360. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project_desktop/client_example.py +0 -0
  361. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project_desktop/client_example.ts +0 -0
  362. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  363. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/sample_project_desktop/server.py +0 -0
  364. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/entity_tests.py +0 -0
  365. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_base_generation.py +0 -0
  366. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  367. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_composite_unique_generation.py +0 -0
  368. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_dto_identity_generation.py +0 -0
  369. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_entity_capability_generation.py +0 -0
  370. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_enum_ordering.py +0 -0
  371. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_excluded_fk_targets.py +0 -0
  372. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_generate_schema.py +0 -0
  373. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_junction_analysis.py +0 -0
  374. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_multi_schema_output.py +0 -0
  375. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_schema_analysis.py +0 -0
  376. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_sql_expression_defaults.py +0 -0
  377. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_table_relationships_query.py +0 -0
  378. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema/test_view_generation.py +0 -0
  379. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema_builder/test_array_default_parsing.py +0 -0
  380. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema_builder/test_enum_array_column.py +0 -0
  381. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  382. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  383. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/schema_builder/test_never_filter_columns.py +0 -0
  384. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/__init__.py +0 -0
  385. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/conftest.py +0 -0
  386. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_a_failed_capture_still_names_the_failure.py +0 -0
  387. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_cache_no_downgrade.py +0 -0
  388. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_capture_poison_proof.py +0 -0
  389. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_coalesce.py +0 -0
  390. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_dag.py +0 -0
  391. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_disk_spill_fallback.py +0 -0
  392. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_flush_individual_fallback.py +0 -0
  393. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_governed_write.py +0 -0
  394. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_managed.py +0 -0
  395. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_op_enqueue_site.py +0 -0
  396. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_reads.py +0 -0
  397. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_record_error_bounded_insert.py +0 -0
  398. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_record_error_storm.py +0 -0
  399. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_session.py +0 -0
  400. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/session/test_spill_guard_plugin.py +0 -0
  401. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_a_backend_handed_over_mid_transaction_never_takes_our_writes.py +0 -0
  402. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_a_bulk_cache_never_stalls_the_loop.py +0 -0
  403. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_a_keyword_column_refuses_generation.py +0 -0
  404. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_a_list_of_uuids_binds_as_uuid_array.py +0 -0
  405. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_a_read_only_backend_never_refuses_our_writes.py +0 -0
  406. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_a_sealed_field_shows_no_character_of_its_value.py +0 -0
  407. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_array_arg_element_type.py +0 -0
  408. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_attachment_mutation_serialization.py +0 -0
  409. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_backup_alias_conflict.py +0 -0
  410. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_backup_history_restore.py +0 -0
  411. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_backup_restore_live.py +0 -0
  412. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_call_function_as.py +0 -0
  413. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_call_function_none_mode.py +0 -0
  414. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_composite_fk_is_not_a_column_fk.py +0 -0
  415. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_database_name_alias.py +0 -0
  416. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_db_functions.py +0 -0
  417. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_dsn_password_masking.py +0 -0
  418. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_dynamic_select_unregistered.py +0 -0
  419. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_foreign_session_state.py +0 -0
  420. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_immutable_insert_or_exact_verify.py +0 -0
  421. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_keyed_digest.py +0 -0
  422. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_model_cls_refactor.py +0 -0
  423. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_native_enable.py +0 -0
  424. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_native_lifecycle.py +0 -0
  425. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_native_lifecycle_fences.py +0 -0
  426. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_native_passkey_receipts.py +0 -0
  427. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_native_source.py +0 -0
  428. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  429. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_one_database.py +0 -0
  430. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_order_by_desc_nulls_last.py +0 -0
  431. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_org_vault_gate.py +0 -0
  432. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_package_wiring_views.py +0 -0
  433. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_pooler_role_guard.py +0 -0
  434. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_secret_audit_names_its_organization.py +0 -0
  435. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_secrets_battery.py +0 -0
  436. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_secrets_minting_keys.py +0 -0
  437. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_shared_lock_guard.py +0 -0
  438. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_sql_param_casts.py +0 -0
  439. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_state_cache_sweeps_stale.py +0 -0
  440. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_statement_ceiling_warning.py +0 -0
  441. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_sync_twins_guard.py +0 -0
  442. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_update_result_truthiness.py +0 -0
  443. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_user_visible_lookup.py +0 -0
  444. {matrx_orm-3.1.233 → matrx_orm-3.1.235}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
@@ -349,3 +349,9 @@ apps/shared/.kit-*-witness.*/
349
349
  .matrx/release-repeats.json.tmp
350
350
  .matrx/fixer-ledger.json
351
351
  /.config-manifest.json
352
+
353
+ # Agents' scratch "proof" copies of shared packages — never tracked (they break the npm planner and the frozen lockfile)
354
+ apps/shared/zz-proof-*/
355
+
356
+ # tsup writes this temp file while it builds a config; never commit it
357
+ tsup.config.bundled_*.mjs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.233
3
+ Version: 3.1.235
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
@@ -26,7 +26,7 @@ Requires-Dist: asyncpg>=0.31.0
26
26
  Requires-Dist: cbor2<7,>=6.1.3
27
27
  Requires-Dist: cryptography>=43.0
28
28
  Requires-Dist: gitpython>=3.1.40
29
- Requires-Dist: matrx-utils>=2.0.75
29
+ Requires-Dist: matrx-utils>=2.0.77
30
30
  Requires-Dist: psycopg-pool>=3.2.5
31
31
  Requires-Dist: psycopg[binary]>=3.2.5
32
32
  Requires-Dist: python-dotenv>=1.0
@@ -0,0 +1,194 @@
1
+ """Session state another client left on a shared server connection.
2
+
3
+ WHY THIS EXISTS. We reach Postgres through Supavisor in TRANSACTION mode: every
4
+ client shares the same pool of server connections, and Supavisor does not reset
5
+ session state between clients. ``pg_dump`` (17.x) begins every run with a
6
+ SESSION-level ``SET restrict_nonsystem_relation_kind = 'view, foreign-table'``.
7
+ A dump pointed at the transaction pooler (port 6543) leaves that setting on
8
+ whichever server connection the ``SET`` landed on, and every later statement of
9
+ ours that touches a view through that connection is refused at parse time::
10
+
11
+ 55000 ObjectNotInPrerequisiteStateError:
12
+ access to non-system view "organization_member" is restricted
13
+
14
+ Measured 2026-10-05 05:49-09:04 UTC: ~50 platform errors (SEO runs list, mandate
15
+ resolution, membership checks, file downloads), each pinned to a few poisoned
16
+ backends that kept failing for minutes.
17
+
18
+ THE DEFENCE (two halves, both here so they cannot drift apart):
19
+
20
+ 1. Every ORM transaction clears the setting transaction-locally at BEGIN
21
+ (:data:`CLEARED_GUCS`, merged into ``transaction._opening_statement_gucs``).
22
+ It rides the one opening statement, so it costs nothing.
23
+ 2. A statement OUTSIDE a transaction cannot carry a ``SET LOCAL`` in the same
24
+ round trip. When one is refused with exactly this parse-time error, it is
25
+ re-run ONCE inside a transaction that clears the setting first. The refusal
26
+ happens during parse/rewrite, before anything executes, so the re-run is
27
+ side-effect free. The cause is logged as a WARNING once per connection so it
28
+ never hides.
29
+
30
+ A BORROWED ROLE IS THE SAME CLASS (2026-10-18). Another client of the tenant ran
31
+ ``set_config('role', 'authenticated', false)`` through the transaction pooler (236
32
+ times, per pg_stat_statements) — session scope, so the server connection went back to
33
+ the shared pool still wearing ``authenticated``. Every server statement routed to it
34
+ ran as a person: ~100 ``permission denied for function claim_*`` /
35
+ ``record_changes_drain`` / ``resolve_full_context`` errors across every worker in three
36
+ bursts. Same two halves:
37
+
38
+ 1. Every ORM transaction opens as OUR login role: ``role = 'none'`` rides the opening
39
+ statement (``set_config('role', 'none', true)`` = ``SET LOCAL ROLE NONE``).
40
+ ``rls_session`` still replaces it with the person's role, and that is LOCAL too.
41
+ 2. A statement outside a transaction refused with 42501 (insufficient privilege) is
42
+ re-run ONCE inside a transaction that resets the role first. A privilege refusal
43
+ rolls the whole single-statement transaction back, so the re-run is side-effect
44
+ free. A borrowed role it observes is logged as an ERROR and recorded durably
45
+ (``pooler_role_contamination``); a genuine denial fails the re-run and propagates.
46
+
47
+ A checkout-time assertion cannot work on this path: in transaction mode each statement
48
+ may land on a different server connection, so the only place a role can be asserted is
49
+ inside the transaction (or single statement) that uses it — which is exactly here.
50
+ """
51
+
52
+ from __future__ import annotations
53
+
54
+ import logging
55
+ import weakref
56
+ from typing import Any, Awaitable, Callable, TypeVar
57
+
58
+ logger = logging.getLogger(__name__)
59
+
60
+ RESTRICT_GUC = "restrict_nonsystem_relation_kind"
61
+
62
+ # Merged into every top-level transaction's opening GUCs. '' = no restriction
63
+ # (the server default); set_config(..., true) scopes it to the transaction.
64
+ # ``role`` = 'none' resets a borrowed role to the session's login role for the
65
+ # transaction (``transaction._opening_statement_gucs`` always emits ``role`` last, and
66
+ # an opener's own ``role`` — rls_session — replaces it).
67
+ CLEARED_GUCS: dict[str, str] = {RESTRICT_GUC: "", "role": "none"}
68
+
69
+ _OBJECT_NOT_IN_PREREQUISITE_STATE = "55000"
70
+ _INSUFFICIENT_PRIVILEGE = "42501"
71
+
72
+ T = TypeVar("T")
73
+
74
+ _warned: "weakref.WeakSet[Any]" = weakref.WeakSet()
75
+ _warned_ids: set[int] = set()
76
+
77
+
78
+ def is_foreign_restrict_refusal(exc: BaseException) -> bool:
79
+ """True iff ``exc`` is the parse-time refusal caused by a leaked restrict GUC."""
80
+ if getattr(exc, "sqlstate", None) != _OBJECT_NOT_IN_PREREQUISITE_STATE:
81
+ return False
82
+ text = str(exc)
83
+ return "access to non-system" in text and "is restricted" in text
84
+
85
+
86
+ def is_privilege_refusal(exc: BaseException) -> bool:
87
+ """True iff ``exc`` is a 42501 — possibly a borrowed role on a shared connection."""
88
+ return getattr(exc, "sqlstate", None) == _INSUFFICIENT_PRIVILEGE
89
+
90
+
91
+ def _warn_once(conn: Any, exc: BaseException, what: str) -> None:
92
+ try:
93
+ if conn in _warned:
94
+ return
95
+ _warned.add(conn)
96
+ except TypeError: # not weak-referenceable
97
+ if id(conn) in _warned_ids:
98
+ return
99
+ _warned_ids.add(id(conn))
100
+ logger.warning(
101
+ "[foreign-session-state] %s was refused because the shared server connection "
102
+ "carries %s from another client (a pg_dump through the transaction pooler sets it "
103
+ "session-wide and Supavisor does not reset it): %s — re-running once in a "
104
+ "transaction that clears it",
105
+ what,
106
+ RESTRICT_GUC,
107
+ exc,
108
+ )
109
+
110
+
111
+ async def clear_in_transaction(conn: Any) -> None:
112
+ """Clear the restrict GUC and any borrowed role for the current transaction on ``conn``."""
113
+ await conn.execute(
114
+ "SELECT set_config($1, $2, true), set_config($3, $4, true)",
115
+ RESTRICT_GUC,
116
+ "",
117
+ "role",
118
+ "none",
119
+ )
120
+
121
+
122
+ async def _report_borrowed_role(borrowed: str, login: str, what: str) -> None:
123
+ logger.error(
124
+ "[foreign-session-state] %s ran on a shared server connection wearing role '%s' "
125
+ "(login role '%s') left by another pooler client's session-scoped role change — "
126
+ "re-ran it as '%s' in a transaction that reset the role",
127
+ what,
128
+ borrowed,
129
+ login,
130
+ login,
131
+ )
132
+ try:
133
+ from matrx_orm.session.fallback import record_error
134
+
135
+ await record_error(
136
+ RuntimeError(
137
+ f"pooled server connection carried borrowed role '{borrowed}' "
138
+ f"(login '{login}'); {what} re-ran as '{login}'"
139
+ ),
140
+ kind="pooler_role_contamination",
141
+ source_app="matrx-orm",
142
+ source_feature="system",
143
+ route="orm:foreign_session_state",
144
+ payload={"borrowed_role": borrowed, "login_role": login, "what": what[:500]},
145
+ )
146
+ except Exception: # noqa: BLE001 — the alarm must never cost the healed call
147
+ logger.exception("[foreign-session-state] could not durably record the borrowed role")
148
+
149
+
150
+ async def run_clearing_foreign_restrict(
151
+ conn: Any, run: Callable[[], Awaitable[T]], *, what: str = "statement"
152
+ ) -> T:
153
+ """Run ``run()`` (one statement on ``conn``, outside any transaction).
154
+
155
+ On the leaked-restrict refusal, or a privilege refusal (a borrowed role), run it
156
+ once more inside a transaction on the same client connection that clears the
157
+ restrict setting and resets the role first. Any other error, or a second
158
+ refusal, propagates unchanged.
159
+ """
160
+ try:
161
+ return await run()
162
+ except Exception as exc:
163
+ privilege = is_privilege_refusal(exc)
164
+ if not privilege and not is_foreign_restrict_refusal(exc):
165
+ raise
166
+ if not privilege:
167
+ _warn_once(conn, exc, what)
168
+ if not privilege:
169
+ async with conn.transaction():
170
+ await clear_in_transaction(conn)
171
+ return await run()
172
+ async with conn.transaction():
173
+ # The transaction pins ONE server connection: read the role it carries, then
174
+ # reset it — both before the re-run, on that same backend.
175
+ row = await conn.fetchrow(
176
+ "SELECT current_user::text AS borrowed, session_user::text AS login"
177
+ )
178
+ await clear_in_transaction(conn)
179
+ result = await run()
180
+ borrowed = row["borrowed"] if row is not None else None
181
+ login = row["login"] if row is not None else None
182
+ if borrowed and login and borrowed != login:
183
+ await _report_borrowed_role(borrowed, login, what)
184
+ return result
185
+
186
+
187
+ __all__ = [
188
+ "CLEARED_GUCS",
189
+ "RESTRICT_GUC",
190
+ "clear_in_transaction",
191
+ "is_foreign_restrict_refusal",
192
+ "is_privilege_refusal",
193
+ "run_clearing_foreign_restrict",
194
+ ]
@@ -198,3 +198,25 @@ def pytest_unconfigure(config: pytest.Config) -> None: # noqa: ARG001
198
198
  _SESSION_SPILL_DIR = None
199
199
  _PREVIOUS_ENV = None
200
200
  _HIJACKS.clear()
201
+
202
+
203
+ @pytest.fixture(autouse=True)
204
+ def _no_live_system_error_rows():
205
+ """Under pytest ``record_error`` never inserts into the live ``ops.system_error``
206
+ (see ``fallback.CAPTURED_UNDER_TEST``); start every test with an empty capture."""
207
+ try:
208
+ from matrx_orm.session import fallback
209
+ except Exception: # noqa: BLE001 — the ORM is not importable in every suite
210
+ yield
211
+ return
212
+ fallback.CAPTURED_UNDER_TEST.clear()
213
+ yield
214
+ fallback.CAPTURED_UNDER_TEST.clear()
215
+
216
+
217
+ @pytest.fixture
218
+ def captured_system_errors() -> list:
219
+ """The ``record_error`` calls this test made, withheld from the live database."""
220
+ from matrx_orm.session import fallback
221
+
222
+ return fallback.CAPTURED_UNDER_TEST
@@ -48,6 +48,15 @@ async def _evict_updated_rows(model_cls: type, rows: list[Any]) -> None:
48
48
  await StateManager.clear_cache(model_cls)
49
49
 
50
50
 
51
+ def _has_pending_in_stack(model_cls: type) -> bool:
52
+ """True when any Session on the stack holds unflushed writes for this model."""
53
+ try:
54
+ from matrx_orm.session import has_pending_in_stack
55
+ except ImportError:
56
+ return False
57
+ return has_pending_in_stack(model_cls)
58
+
59
+
51
60
  def _merge_pending_into_models(model_cls: type, db_models: list, filters: dict[str, Any]) -> list:
52
61
  """Apply pending Session state to a list of Model instances from DB.
53
62
 
@@ -945,9 +954,14 @@ class QueryBuilder(Generic[ModelT]):
945
954
  async def first(self) -> ModelT | None:
946
955
  """Get the first matching record.
947
956
 
948
- Session-aware via the same merge pathway as :meth:`all`."""
957
+ Session-aware via the same merge pathway as :meth:`all`. With no pending
958
+ Session writes for this model the database is asked for one row
959
+ (``LIMIT 1``); fetching every row cost the DB host recorder a 50 MB,
960
+ 780 ms read of its whole history every minute (2026-10-17)."""
949
961
  try:
950
962
  executor = await self._get_executor_async()
963
+ if not _has_pending_in_stack(self.model):
964
+ return await executor.first()
951
965
  results = await executor.all()
952
966
  merged = _merge_pending_into_models(
953
967
  self.model,
@@ -651,6 +651,14 @@ async def _bulk_insert_failures(
651
651
  return failed
652
652
 
653
653
 
654
+ #: Every ``record_error`` call made under pytest whose real insert was withheld.
655
+ CAPTURED_UNDER_TEST: list[dict[str, Any]] = []
656
+
657
+
658
+ def under_pytest() -> bool:
659
+ return "PYTEST_CURRENT_TEST" in os.environ
660
+
661
+
654
662
  async def record_error(
655
663
  exc: BaseException,
656
664
  *,
@@ -756,6 +764,29 @@ async def record_error(
756
764
  },
757
765
  }
758
766
 
767
+ if _insert_error is _LIVE_INSERT_ERROR and under_pytest():
768
+ # THE LIVE ERROR LOG IS NOT A TEST FIXTURE (2026-10-17). Unit tests with fake
769
+ # cleaners filed 14 real ``rag_page_clean_degraded`` rows into ops.system_error
770
+ # through this door. Under pytest the real insert never runs: the call is kept
771
+ # in ``CAPTURED_UNDER_TEST`` (the ``captured_system_errors`` fixture reads it).
772
+ # A test that patches ``_insert_error`` itself is isolated by its own double and
773
+ # still reaches it; ``_insert_error`` called directly still takes its own adapter.
774
+ CAPTURED_UNDER_TEST.append(
775
+ {
776
+ "kind": kind,
777
+ "error_type": resolved_error_type,
778
+ "error_text": resolved_error_text,
779
+ "user_id": user_id,
780
+ "organization_id": organization_id,
781
+ "source_app": source_app,
782
+ "source_feature": source_feature,
783
+ "route": route,
784
+ "context": context,
785
+ "payload": payload,
786
+ }
787
+ )
788
+ return
789
+
759
790
  try:
760
791
  await _insert_error(
761
792
  kind=kind,
@@ -915,6 +946,9 @@ async def _insert_error(
915
946
  )
916
947
 
917
948
 
949
+
950
+ _LIVE_INSERT_ERROR = _insert_error
951
+
918
952
  async def _execute_error_insert(
919
953
  conn: Any,
920
954
  *,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.1.233"
3
+ version = "3.1.235"
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" }
@@ -41,7 +41,7 @@ dependencies = [
41
41
  "gitpython>=3.1.40",
42
42
  "psycopg[binary]>=3.2.5",
43
43
  "psycopg-pool>=3.2.5",
44
- "matrx-utils>=2.0.75",
44
+ "matrx-utils>=2.0.77",
45
45
  "pyyaml>=6.0.3",
46
46
  "python-dotenv>=1.0",
47
47
  "cryptography>=43.0",
@@ -0,0 +1,279 @@
1
+ """A pooled server connection left wearing a person's role never runs our server calls.
2
+
3
+ THE LIVE DEFECT (2026-10-18, bursts 04:58, 05:54, 07:49 UTC). ~100 ops.system_error rows
4
+ across every worker at once — sms_dispatcher, notification_dispatcher, record-changes
5
+ drain, workflow lease reaper, request audit, and an Agent run (``call_function
6
+ resolve_full_context`` via ``run_clearing_foreign_restrict``) — all
7
+ ``permission denied for function ...`` on functions whose EXECUTE only postgres /
8
+ service_role hold. pg_stat_statements named the source: a client of the same tenant ran
9
+
10
+ select set_config('request.jwt.claims', ..., false), ...,
11
+ set_config('role', 'authenticated', false)
12
+
13
+ 236 times through the TRANSACTION pooler. ``false`` = session scope: Supavisor hands that
14
+ server connection back to the shared pool still wearing ``authenticated``, and our next
15
+ statement routed to it runs as a person.
16
+
17
+ THE CLASS: any server-role statement (a statement outside a transaction, or a top-level
18
+ transaction without ``rls_session``) inherits whatever role the shared backend carries.
19
+ The ORM's own scopes are transaction-local and cannot leak (proved below too); the
20
+ defence must hold against a backend another client dirtied.
21
+
22
+ ``ServerBackend`` models one Supavisor server connection with real Postgres semantics:
23
+ session-scope vs transaction-local ``role``, ``none`` = the login role, a server-only
24
+ function that refuses every role but ``postgres``.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import asyncio
30
+ import re
31
+ from contextlib import asynccontextmanager
32
+ from typing import Any
33
+
34
+ import asyncpg
35
+ import pytest
36
+
37
+ from matrx_orm.adapters import AdapterRegistry
38
+ from matrx_orm.core import db_function
39
+ from matrx_orm.core import foreign_session_state as fss
40
+ from matrx_orm.core.async_db_manager import AsyncDatabaseManager
41
+ from matrx_orm.core.rls_session import rls_session
42
+ from matrx_orm.core.session_context import configure_session_context
43
+ from matrx_orm.core.transaction import _opening_statement_gucs, transaction
44
+
45
+ LOGIN = "postgres"
46
+ SERVER_ONLY = "claim_pending_sms_outbound_attempts"
47
+
48
+
49
+ def _denied(fn: str) -> Exception:
50
+ exc = asyncpg.exceptions.InsufficientPrivilegeError(f"permission denied for function {fn}")
51
+ exc.sqlstate = "42501"
52
+ return exc
53
+
54
+
55
+ class ServerBackend:
56
+ """One shared pooler server connection, as Postgres would behave."""
57
+
58
+ def __init__(self) -> None:
59
+ self.session_role: str | None = None # None = login role
60
+ self.local_role: str | None = None
61
+ self.in_tx = False
62
+ self.server_calls = 0
63
+ self.denials = 0
64
+ self.statements: list[str] = []
65
+
66
+ # --- Postgres semantics -------------------------------------------------
67
+ @property
68
+ def current_user(self) -> str:
69
+ role = self.local_role if self.local_role is not None else self.session_role
70
+ return LOGIN if role in (None, "none") else role
71
+
72
+ def _apply_set_config(self, sql: str, args: tuple[Any, ...]) -> None:
73
+ pairs: list[tuple[str, str, bool]] = []
74
+ # bound pairs: set_config($n, $m, true)
75
+ bound = re.findall(r"set_config\(\$(\d+),\s*\$(\d+),\s*(true|false)\)", sql)
76
+ for name_i, value_i, local in bound:
77
+ pairs.append((args[int(name_i) - 1], args[int(value_i) - 1], local == "true"))
78
+ literal = re.findall(r"set_config\('([^']+)',\s*'([^']*)',\s*(true|false)\)", sql)
79
+ for name, value, local in literal:
80
+ pairs.append((name, value, local == "true"))
81
+ for name, value, is_local in pairs:
82
+ if name != "role":
83
+ continue
84
+ if is_local:
85
+ if self.in_tx: # outside a transaction a LOCAL set dies with the statement
86
+ self.local_role = value
87
+ else:
88
+ self.session_role = value
89
+
90
+ def _end_tx(self) -> None:
91
+ self.in_tx = False
92
+ self.local_role = None
93
+
94
+ async def execute(self, sql: str, *args: Any, timeout: Any = None) -> str: # noqa: ASYNC109 — mirrors asyncpg's connection signature
95
+ self.statements.append(sql)
96
+ s = sql.strip().upper()
97
+ if s == "BEGIN":
98
+ self.in_tx = True
99
+ elif s in ("COMMIT", "ROLLBACK"):
100
+ self._end_tx()
101
+ elif "SET_CONFIG" in s:
102
+ self._apply_set_config(sql, args)
103
+ elif SERVER_ONLY in sql:
104
+ self._server_only()
105
+ return "OK"
106
+
107
+ def _server_only(self) -> list[dict[str, Any]]:
108
+ if self.current_user != LOGIN:
109
+ self.denials += 1
110
+ raise _denied(SERVER_ONLY)
111
+ self.server_calls += 1
112
+ return [{"id": "row-1"}]
113
+
114
+ async def fetch(self, sql: str, *args: Any, timeout: Any = None) -> list[dict[str, Any]]: # noqa: ASYNC109 — mirrors asyncpg's connection signature
115
+ self.statements.append(sql)
116
+ if "set_config" in sql:
117
+ self._apply_set_config(sql, args)
118
+ return [{}]
119
+ if SERVER_ONLY in sql:
120
+ return self._server_only()
121
+ return []
122
+
123
+ async def fetchval(self, sql: str, *args: Any, timeout: Any = None) -> Any: # noqa: ASYNC109 — mirrors asyncpg's connection signature
124
+ self.statements.append(sql)
125
+ if "current_user" in sql:
126
+ return self.current_user
127
+ if "set_config" in sql:
128
+ self._apply_set_config(sql, args)
129
+ return ""
130
+ if SERVER_ONLY in sql:
131
+ return self._server_only()[0]["id"]
132
+ return None
133
+
134
+ async def fetchrow(self, sql: str, *args: Any, timeout: Any = None) -> Any: # noqa: ASYNC109 — mirrors asyncpg's connection signature
135
+ if "set_config" in sql:
136
+ self._apply_set_config(sql, args)
137
+ if "current_user" in sql:
138
+ return {
139
+ "current_user": self.current_user,
140
+ "session_user": LOGIN,
141
+ "borrowed": self.current_user,
142
+ "login": LOGIN,
143
+ }
144
+ return {}
145
+
146
+ @asynccontextmanager
147
+ async def transaction(self): # asyncpg Connection.transaction()
148
+ self.in_tx = True
149
+ try:
150
+ yield
151
+ finally:
152
+ self._end_tx()
153
+
154
+ def terminate(self) -> None:
155
+ pass
156
+
157
+ # --- the foreign client -------------------------------------------------
158
+ def foreign_client_leaks_person_role(self) -> None:
159
+ """What pg_stat_statements caught: set_config('role','authenticated',false)."""
160
+ self._apply_set_config("select set_config('role','authenticated',false)", ())
161
+
162
+
163
+ @pytest.fixture
164
+ def backend(monkeypatch) -> ServerBackend:
165
+ b = ServerBackend()
166
+
167
+ @asynccontextmanager
168
+ async def get_connection(_config_name: Any, _timeout: Any = 10.0):
169
+ yield b
170
+
171
+ class _Adapter:
172
+ def get_connection(self):
173
+ return get_connection(None)
174
+
175
+ async def no_driver_timeout(_conn, **_kw):
176
+ return None
177
+
178
+ monkeypatch.setattr(AsyncDatabaseManager, "get_connection", get_connection)
179
+ monkeypatch.setattr(AdapterRegistry, "get", lambda _db: _Adapter())
180
+ monkeypatch.setattr(db_function, "resolve_driver_timeout", no_driver_timeout)
181
+ configure_session_context(None)
182
+ return b
183
+
184
+
185
+ async def _server_call() -> Any:
186
+ return await db_function.call_function("platform", "communication", SERVER_ONLY, "w1", mode="rows")
187
+
188
+
189
+ # ---------------------------------------------------------------------------
190
+ # RED before the fix: a server call outside a transaction on a dirtied backend.
191
+ # ---------------------------------------------------------------------------
192
+ async def test_server_call_on_a_backend_left_wearing_a_person_role_runs_as_the_server(backend) -> None:
193
+ backend.foreign_client_leaks_person_role()
194
+ assert backend.current_user == "authenticated"
195
+
196
+ rows = await _server_call()
197
+
198
+ assert rows == [{"id": "row-1"}]
199
+ assert backend.server_calls == 1
200
+ # Repaired transaction-locally only — the session role is not ours to change.
201
+ assert backend.session_role == "authenticated"
202
+
203
+
204
+ async def test_server_transaction_on_a_dirtied_backend_runs_as_the_login_role(backend) -> None:
205
+ backend.foreign_client_leaks_person_role()
206
+ async with transaction("platform"):
207
+ rows = await _server_call()
208
+ assert rows == [{"id": "row-1"}]
209
+ assert backend.denials == 0 # the BEGIN itself reset the borrowed role
210
+
211
+
212
+ def test_every_transaction_opens_as_the_login_role_unless_rls_session_says_otherwise() -> None:
213
+ gucs = _opening_statement_gucs({})
214
+ assert gucs["role"] == "none"
215
+ assert list(gucs)[-1] == "role"
216
+ assert _opening_statement_gucs({"role": "authenticated"})["role"] == "authenticated"
217
+
218
+
219
+ # ---------------------------------------------------------------------------
220
+ # The person scope ending abnormally never leaves its role behind (our own scope).
221
+ # ---------------------------------------------------------------------------
222
+ async def test_rls_session_ended_by_an_exception_leaves_no_role(backend) -> None:
223
+ with pytest.raises(RuntimeError):
224
+ async with rls_session({"sub": "u1", "role": "authenticated"}, role="authenticated", database="platform"):
225
+ assert backend.current_user == "authenticated"
226
+ raise RuntimeError("boom")
227
+ assert backend.current_user == LOGIN
228
+ assert await _server_call() == [{"id": "row-1"}]
229
+ assert backend.denials == 0
230
+
231
+
232
+ async def test_rls_session_ended_by_cancellation_leaves_no_role(backend) -> None:
233
+ entered = asyncio.Event()
234
+
235
+ async def person_work() -> None:
236
+ async with rls_session({"sub": "u1", "role": "authenticated"}, role="authenticated", database="platform"):
237
+ entered.set()
238
+ await asyncio.sleep(10)
239
+
240
+ task = asyncio.create_task(person_work())
241
+ await entered.wait()
242
+ task.cancel()
243
+ with pytest.raises(asyncio.CancelledError):
244
+ await task
245
+ assert backend.current_user == LOGIN
246
+ assert await _server_call() == [{"id": "row-1"}]
247
+ assert backend.denials == 0
248
+
249
+
250
+ async def test_the_foreign_restrict_path_then_a_borrowed_role_still_heals(backend) -> None:
251
+ """The live Agent traceback went through run_clearing_foreign_restrict."""
252
+ backend.foreign_client_leaks_person_role()
253
+ value = await fss.run_clearing_foreign_restrict(
254
+ backend, lambda: backend.fetchval(f"select {SERVER_ONLY}()"), what="t"
255
+ )
256
+ assert value == "row-1"
257
+
258
+
259
+ # ---------------------------------------------------------------------------
260
+ # Never hides a real denial; always loud when it repairs.
261
+ # ---------------------------------------------------------------------------
262
+ async def test_a_genuine_denial_still_raises_after_one_rerun(backend) -> None:
263
+ calls = 0
264
+
265
+ async def run() -> Any:
266
+ nonlocal calls
267
+ calls += 1
268
+ raise _denied("auth_secret")
269
+
270
+ with pytest.raises(asyncpg.exceptions.InsufficientPrivilegeError):
271
+ await fss.run_clearing_foreign_restrict(backend, run)
272
+ assert calls == 2
273
+
274
+
275
+ async def test_the_repair_is_loud(backend, caplog) -> None:
276
+ backend.foreign_client_leaks_person_role()
277
+ with caplog.at_level("ERROR"):
278
+ await _server_call()
279
+ assert any("authenticated" in r.getMessage() for r in caplog.records)
@@ -0,0 +1,37 @@
1
+ """Guard: a test run must never file a row in the live ops.system_error.
2
+
3
+ Incident 2026-10-16/17: 14 ``rag_page_clean_degraded`` rows landed in the live error
4
+ log from unit tests with fake cleaners, via ``matrx_orm.record_error``.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import pytest
10
+
11
+ from matrx_orm.session import fallback
12
+
13
+
14
+ @pytest.mark.asyncio
15
+ async def test_record_error_under_pytest_never_reaches_the_database(monkeypatch, captured_system_errors):
16
+ def _boom(*a, **k):
17
+ raise AssertionError("record_error reached the live database path under pytest")
18
+
19
+ # If the guard failed, the real _insert_error would resolve an adapter first.
20
+ monkeypatch.setattr("matrx_orm.adapters.AdapterRegistry.get", _boom)
21
+ await fallback.record_error(
22
+ RuntimeError("synthetic"), kind="guard_probe", source_feature="tests.guard"
23
+ )
24
+ assert [c["kind"] for c in captured_system_errors] == ["guard_probe"]
25
+
26
+
27
+ @pytest.mark.asyncio
28
+ async def test_the_guard_is_falsifiable_a_patched_insert_is_still_reached(monkeypatch, captured_system_errors):
29
+ seen = []
30
+
31
+ async def _fake(**kw):
32
+ seen.append(kw["kind"])
33
+
34
+ monkeypatch.setattr(fallback, "_insert_error", _fake)
35
+ await fallback.record_error(RuntimeError("x"), kind="patched_probe")
36
+ assert seen == ["patched_probe"]
37
+ assert captured_system_errors == []