matrx-orm 3.1.174__tar.gz → 3.1.182__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 (414) hide show
  1. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/.gitignore +10 -0
  2. matrx_orm-3.1.182/CHANGELOG.md +6 -0
  3. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/PKG-INFO +2 -2
  4. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/__init__.py +12 -1
  5. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/catalog_sql.py +40 -0
  6. matrx_orm-3.1.182/matrx_orm/core/admin_lane.py +47 -0
  7. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/expressions.py +9 -0
  8. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/fields.py +26 -5
  9. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/introspection.py +76 -2
  10. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/pooler_role.py +1 -0
  11. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/rls_session.py +30 -0
  12. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/diff.py +15 -0
  13. matrx_orm-3.1.182/matrx_orm/pytest_live_db_guard.py +190 -0
  14. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/pytest_lock_guard.py +96 -12
  15. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/python_sql/db_objects.py +16 -0
  16. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/python_sql/table_detailed_relationships.py +14 -0
  17. matrx_orm-3.1.182/matrx_orm/schema_builder/FEATURE.md +42 -0
  18. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/FEATURE.md +17 -0
  19. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/__init__.py +17 -0
  20. matrx_orm-3.1.182/matrx_orm/secrets_battery/backup_restore.py +286 -0
  21. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/crypto.py +13 -0
  22. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/items.py +51 -3
  23. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/mutation_receipts.py +39 -2
  24. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/org_service.py +7 -4
  25. matrx_orm-3.1.182/matrx_orm/secrets_battery/vault_history.py +148 -0
  26. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/README.md +11 -1
  27. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/fallback.py +105 -26
  28. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/lifecycle.py +1 -0
  29. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/managed.py +5 -2
  30. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/session.py +27 -0
  31. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/pyproject.toml +3 -2
  32. matrx_orm-3.1.182/tests/level1/test_fk_cycle_resolution.py +122 -0
  33. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_from_alias.py +69 -0
  34. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_org_assignment_inventory.py +60 -20
  35. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_rls_session_scope.py +58 -7
  36. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_watched_lifecycle.py +2 -0
  37. matrx_orm-3.1.182/tests/session/test_a_failed_capture_still_names_the_failure.py +144 -0
  38. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_flush_individual_fallback.py +34 -0
  39. matrx_orm-3.1.182/tests/session/test_live_db_guard_plugin.py +103 -0
  40. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_record_error_bounded_insert.py +22 -2
  41. matrx_orm-3.1.182/tests/test_backup_alias_conflict.py +44 -0
  42. matrx_orm-3.1.182/tests/test_backup_restore_live.py +192 -0
  43. matrx_orm-3.1.182/tests/test_composite_fk_is_not_a_column_fk.py +202 -0
  44. matrx_orm-3.1.182/tests/test_dsn_password_masking.py +113 -0
  45. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_native_passkey_receipts.py +23 -0
  46. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_secrets_battery.py +109 -0
  47. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_shared_lock_guard.py +77 -13
  48. matrx_orm-3.1.174/matrx_orm/schema_builder/FEATURE.md +0 -23
  49. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/AGENTS.md +0 -0
  50. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/BASE_CLASS_METHODS.md +0 -0
  51. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/CLAUDE.md +0 -0
  52. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/MODEL_API.md +0 -0
  53. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/README.md +0 -0
  54. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/database/orm/extended/managers/ai_model_base.py +0 -0
  55. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/docs/migrations.md +0 -0
  56. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/FEATURE.md +0 -0
  57. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/MODULE_README.md +0 -0
  58. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/adapters/MODULE_README.md +0 -0
  59. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/adapters/__init__.py +0 -0
  60. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/adapters/async_postgresql.py +0 -0
  61. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/adapters/base_adapter.py +0 -0
  62. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  63. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/adapters/supabase_adapter.py +0 -0
  64. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/admin/FEATURE.md +0 -0
  65. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/admin/__init__.py +0 -0
  66. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/admin/router.py +0 -0
  67. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/api/__init__.py +0 -0
  68. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/api/auth.py +0 -0
  69. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/api/config.py +0 -0
  70. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/api/handlers.py +0 -0
  71. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/api/protocol.py +0 -0
  72. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/api/server.py +0 -0
  73. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/associations.py +0 -0
  74. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/cache_debug.py +0 -0
  75. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/catalog.py +0 -0
  76. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/client/MODULE_README.md +0 -0
  77. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/client/__init__.py +0 -0
  78. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/client/postgres_connection.py +0 -0
  79. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/client/postgrest.py +0 -0
  80. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/client/supabase_auth.py +0 -0
  81. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/client/supabase_config.py +0 -0
  82. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/client/supabase_manager.py +0 -0
  83. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  84. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/FEATURE.md +0 -0
  85. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/MODULE_README.md +0 -0
  86. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  87. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/TASKS.md +0 -0
  88. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/__init__.py +0 -0
  89. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/async_db_manager.py +0 -0
  90. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/base.py +0 -0
  91. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/config.py +0 -0
  92. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/connections.py +0 -0
  93. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/db_function.py +0 -0
  94. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/diagnostics.py +0 -0
  95. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/extended.py +0 -0
  96. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/listen.py +0 -0
  97. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/loop_filters.py +0 -0
  98. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/model_dto.py +0 -0
  99. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/model_view.py +0 -0
  100. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/one_database.py +0 -0
  101. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/paginator.py +0 -0
  102. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/pool_maintenance.py +0 -0
  103. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/pool_watch.py +0 -0
  104. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/pydantic_bridge.py +0 -0
  105. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/registry.py +0 -0
  106. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/relations.py +0 -0
  107. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/resilience.py +0 -0
  108. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/session_context.py +0 -0
  109. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/signals.py +0 -0
  110. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/tls.py +0 -0
  111. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/transaction.py +0 -0
  112. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/types.py +0 -0
  113. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/write_queue.py +0 -0
  114. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/core/write_retry.py +0 -0
  115. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/entity.py +0 -0
  116. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/error_handling.py +0 -0
  117. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/exceptions.py +0 -0
  118. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/extended/__init__.py +0 -0
  119. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/extended/app_error_handler.py +0 -0
  120. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/identity_rule.py +0 -0
  121. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/local_sqlite.py +0 -0
  122. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/middleware/__init__.py +0 -0
  123. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/middleware/base.py +0 -0
  124. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/MODULE_README.md +0 -0
  125. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/__init__.py +0 -0
  126. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/cli.py +0 -0
  127. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/ddl.py +0 -0
  128. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/executor.py +0 -0
  129. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/integration.py +0 -0
  130. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/loader.py +0 -0
  131. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/operations.py +0 -0
  132. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/state.py +0 -0
  133. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/migrations/table_filter.py +0 -0
  134. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/no_org_assignment.py +0 -0
  135. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/FEATURE.md +0 -0
  136. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/MODULE_README.md +0 -0
  137. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/__init__.py +0 -0
  138. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/bulk_update_values.py +0 -0
  139. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/catalog_select.py +0 -0
  140. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/conflict.py +0 -0
  141. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/conflict_writes.py +0 -0
  142. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/create.py +0 -0
  143. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/db_functions.py +0 -0
  144. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/delete.py +0 -0
  145. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/dynamic_admin.py +0 -0
  146. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/dynamic_crud.py +0 -0
  147. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/graph_walk.py +0 -0
  148. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/latest_rows.py +0 -0
  149. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/queue_claim.py +0 -0
  150. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/read.py +0 -0
  151. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/relational_backfill.py +0 -0
  152. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/staging_load.py +0 -0
  153. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/operations/update.py +0 -0
  154. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/platform_access.py +0 -0
  155. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/platform_db.py +0 -0
  156. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/py.typed +0 -0
  157. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/pytest_plugin.py +0 -0
  158. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/python_sql/MODULE_README.md +0 -0
  159. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/python_sql/__init__.py +0 -0
  160. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  161. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/query/FEATURE.md +0 -0
  162. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/query/MODULE_README.md +0 -0
  163. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/query/__init__.py +0 -0
  164. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/query/builder.py +0 -0
  165. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/query/executor.py +0 -0
  166. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/resources/__init__.py +0 -0
  167. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
  168. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
  169. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  170. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/__init__.py +0 -0
  171. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/code_handler.py +0 -0
  172. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  173. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/columns.py +0 -0
  174. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/common.py +0 -0
  175. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/diff_preview.py +0 -0
  176. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  177. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/generator.py +0 -0
  178. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  179. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  180. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  181. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  182. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/package_wiring.py +0 -0
  183. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/relationships.py +0 -0
  184. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/runner.py +0 -0
  185. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/schema.py +0 -0
  186. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/schema_manager.py +0 -0
  187. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/tables.py +0 -0
  188. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/schema_builder/views.py +0 -0
  189. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/attachments.py +0 -0
  190. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  191. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/model.py +0 -0
  192. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/native_enable.py +0 -0
  193. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
  194. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
  195. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/native_source.py +0 -0
  196. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/secrets_battery/service.py +0 -0
  197. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/__init__.py +0 -0
  198. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/coalesce.py +0 -0
  199. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/dag.py +0 -0
  200. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/errors.py +0 -0
  201. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/flush.py +0 -0
  202. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/op.py +0 -0
  203. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/reads.py +0 -0
  204. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/session/telemetry.py +0 -0
  205. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  206. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/sql_executor/__init__.py +0 -0
  207. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/sql_executor/executor.py +0 -0
  208. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/sql_executor/queries.py +0 -0
  209. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/sql_executor/registry.py +0 -0
  210. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/sql_executor/types.py +0 -0
  211. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/sql_executor/utils.py +0 -0
  212. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/state.py +0 -0
  213. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/utils/__init__.py +0 -0
  214. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/utils/sql_utils.py +0 -0
  215. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/utils/type_converters.py +0 -0
  216. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/matrx_orm/write_actor.py +0 -0
  217. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/scripts/git-branches.sh +0 -0
  218. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/scripts/publish.sh +0 -0
  219. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/scripts/release.sh +0 -0
  220. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/MODULE_README.md +0 -0
  221. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/__init__.py +0 -0
  222. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/conftest.py +0 -0
  223. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/MODULE_README.md +0 -0
  224. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/__init__.py +0 -0
  225. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_adapter_registry.py +0 -0
  226. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_admin_db_columns.py +0 -0
  227. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_admin_response_rendering.py +0 -0
  228. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_admin_search_negation.py +0 -0
  229. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_admin_search_sentinels.py +0 -0
  230. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_advisory_lock.py +0 -0
  231. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_agent_message.py +0 -0
  232. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_api_auth.py +0 -0
  233. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_api_config.py +0 -0
  234. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_api_handlers.py +0 -0
  235. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_api_protocol.py +0 -0
  236. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_array_agg_order_by.py +0 -0
  237. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_association_trigger_actor_naming.py +0 -0
  238. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_associations.py +0 -0
  239. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_associations_wiring.py +0 -0
  240. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_bulk_hydration_offload.py +0 -0
  241. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_bulk_update_by_pk.py +0 -0
  242. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  243. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_cache_debug.py +0 -0
  244. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_call_function.py +0 -0
  245. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_case_when.py +0 -0
  246. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_catalog.py +0 -0
  247. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_committed_row_guard.py +0 -0
  248. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_composite_pk_filter.py +0 -0
  249. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_composite_pk_write_paths.py +0 -0
  250. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_config.py +0 -0
  251. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_conflict_target.py +0 -0
  252. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_conflict_writes.py +0 -0
  253. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_connection_codecs.py +0 -0
  254. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_connection_poison_guard.py +0 -0
  255. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_connection_primitives.py +0 -0
  256. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_count_composite_distinct.py +0 -0
  257. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_count_limit_param_leak.py +0 -0
  258. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_db_function_field.py +0 -0
  259. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_ddl_generator.py +0 -0
  260. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_deadlock_retry.py +0 -0
  261. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_derived_table.py +0 -0
  262. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_dirty_tracking.py +0 -0
  263. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_dynamic_crud.py +0 -0
  264. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_exceptions.py +0 -0
  265. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  266. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_expression_primitives.py +0 -0
  267. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_fields.py +0 -0
  268. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  269. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_fts.py +0 -0
  270. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_func_expression_args.py +0 -0
  271. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_graph_walk.py +0 -0
  272. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_having_annotate_alias.py +0 -0
  273. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  274. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_introspect_rls_policies.py +0 -0
  275. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_join_and_aggregate.py +0 -0
  276. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_latest_rows.py +0 -0
  277. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_listen_notify.py +0 -0
  278. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_local_sqlite.py +0 -0
  279. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_lock_contention_retry.py +0 -0
  280. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_managed_write_guard.py +0 -0
  281. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_matrx_entity.py +0 -0
  282. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_migration_diff_types.py +0 -0
  283. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_migration_loader.py +0 -0
  284. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_model_instance.py +0 -0
  285. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_model_meta.py +0 -0
  286. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_optimistic_concurrency.py +0 -0
  287. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_order_by_expression.py +0 -0
  288. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
  289. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_parameter_name_hydration.py +0 -0
  290. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_patch_jsonb_path.py +0 -0
  291. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_percentile_cont.py +0 -0
  292. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_persisted_count.py +0 -0
  293. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_pool_holder_census.py +0 -0
  294. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_pool_loop_recreation.py +0 -0
  295. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_pool_maintenance.py +0 -0
  296. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_postgrest_filters.py +0 -0
  297. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_pydantic_bridge.py +0 -0
  298. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_query_builder.py +0 -0
  299. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_query_error_detail.py +0 -0
  300. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_query_executor_sql.py +0 -0
  301. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_query_timeout.py +0 -0
  302. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  303. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_queue_claim.py +0 -0
  304. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_read_connection_retry.py +0 -0
  305. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_registry.py +0 -0
  306. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_registry_multi_database.py +0 -0
  307. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_registry_multi_schema.py +0 -0
  308. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_relational_backfill.py +0 -0
  309. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_relations.py +0 -0
  310. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_resilience.py +0 -0
  311. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_schema_exists.py +0 -0
  312. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_session_advisory_lock.py +0 -0
  313. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_standalone_write_session_context.py +0 -0
  314. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_state_cache.py +0 -0
  315. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_subquery_filter_raw.py +0 -0
  316. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_subquery_in_filter.py +0 -0
  317. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_subquery_nested_expressions.py +0 -0
  318. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_supabase_auth.py +0 -0
  319. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_supabase_config.py +0 -0
  320. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_tls.py +0 -0
  321. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_transaction_database_identity.py +0 -0
  322. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_typed_join_projection.py +0 -0
  323. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_typed_predicates.py +0 -0
  324. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_update_case_expression.py +0 -0
  325. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_update_subquery.py +0 -0
  326. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_update_with_rebase.py +0 -0
  327. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_upsert_default_leak.py +0 -0
  328. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_upsert_with_conflict.py +0 -0
  329. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_values_jsonb_decode.py +0 -0
  330. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_write_queue_deadline.py +0 -0
  331. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level1/test_write_retry_semantics.py +0 -0
  332. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/MODULE_README.md +0 -0
  333. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/__init__.py +0 -0
  334. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/conftest.py +0 -0
  335. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_bulk_ops.py +0 -0
  336. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_cache_integration.py +0 -0
  337. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_crud.py +0 -0
  338. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_foreign_keys.py +0 -0
  339. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_m2m.py +0 -0
  340. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_manager.py +0 -0
  341. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_migrations_live.py +0 -0
  342. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_query_execution.py +0 -0
  343. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/test_schema_diff.py +0 -0
  344. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
  345. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/live_pooler_role.py +0 -0
  346. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/native_source_cases.py +0 -0
  347. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/native_source_oracle.rs +0 -0
  348. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project/.env.example +0 -0
  349. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project/README.md +0 -0
  350. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project/__init__.py +0 -0
  351. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project/generate.py +0 -0
  352. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project/generated/.gitkeep +0 -0
  353. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project/matrx_orm.yaml +0 -0
  354. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project/test_schema_generation.py +0 -0
  355. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project_desktop/.env.example +0 -0
  356. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project_desktop/README.md +0 -0
  357. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project_desktop/__init__.py +0 -0
  358. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project_desktop/client_example.py +0 -0
  359. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project_desktop/client_example.ts +0 -0
  360. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  361. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/sample_project_desktop/server.py +0 -0
  362. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/entity_tests.py +0 -0
  363. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_base_generation.py +0 -0
  364. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  365. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_composite_unique_generation.py +0 -0
  366. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_dto_identity_generation.py +0 -0
  367. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_entity_capability_generation.py +0 -0
  368. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_enum_ordering.py +0 -0
  369. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_generate_schema.py +0 -0
  370. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_junction_analysis.py +0 -0
  371. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_multi_schema_output.py +0 -0
  372. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_schema_analysis.py +0 -0
  373. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_sql_expression_defaults.py +0 -0
  374. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_table_relationships_query.py +0 -0
  375. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema/test_view_generation.py +0 -0
  376. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema_builder/test_array_default_parsing.py +0 -0
  377. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  378. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  379. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/__init__.py +0 -0
  380. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/conftest.py +0 -0
  381. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_cache_no_downgrade.py +0 -0
  382. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_capture_poison_proof.py +0 -0
  383. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_coalesce.py +0 -0
  384. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_dag.py +0 -0
  385. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_disk_spill_fallback.py +0 -0
  386. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_governed_write.py +0 -0
  387. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_managed.py +0 -0
  388. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_op_enqueue_site.py +0 -0
  389. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_reads.py +0 -0
  390. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_record_error_storm.py +0 -0
  391. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_session.py +0 -0
  392. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/session/test_spill_guard_plugin.py +0 -0
  393. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_array_arg_element_type.py +0 -0
  394. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_attachment_mutation_serialization.py +0 -0
  395. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_database_name_alias.py +0 -0
  396. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_db_functions.py +0 -0
  397. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_dynamic_select_unregistered.py +0 -0
  398. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_immutable_insert_or_exact_verify.py +0 -0
  399. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_keyed_digest.py +0 -0
  400. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_model_cls_refactor.py +0 -0
  401. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_native_enable.py +0 -0
  402. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_native_lifecycle.py +0 -0
  403. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_native_lifecycle_fences.py +0 -0
  404. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_native_source.py +0 -0
  405. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  406. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_one_database.py +0 -0
  407. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_order_by_desc_nulls_last.py +0 -0
  408. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_pooler_role_guard.py +0 -0
  409. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_secret_audit_names_its_organization.py +0 -0
  410. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_secrets_minting_keys.py +0 -0
  411. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_sql_param_casts.py +0 -0
  412. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_sync_twins_guard.py +0 -0
  413. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_user_visible_lookup.py +0 -0
  414. {matrx_orm-3.1.174 → matrx_orm-3.1.182}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
@@ -197,6 +197,14 @@ cython_debug/
197
197
  # Frontend pnpm workspace (apps/) — node_modules at the workspace root and any
198
198
  # member, plus Vite caches and build output. The unified lockfile (apps/pnpm-lock.yaml)
199
199
  # IS committed; everything below is regenerated.
200
+ #
201
+ # 🚨 BOTH SPELLINGS. `node_modules/` with the slash matches a DIRECTORY only, so
202
+ # a SYMLINK named `node_modules` slipped straight past it: five of them — each
203
+ # an absolute path into one machine's checkout — were committed on main and
204
+ # broke `pnpm install --frozen-lockfile` on every CI runner, where the symlink
205
+ # is dangling and pnpm's `mkdir` of it answers ENOENT. Every package release was
206
+ # blocked by it. The bare spelling below matches the symlink too.
207
+ node_modules
200
208
  node_modules/
201
209
  apps/**/.vite/
202
210
  apps/**/dist/
@@ -328,3 +336,5 @@ apps/shared/.kit-*-witness.*/
328
336
  # They live at .wt/<slug> (disk-groom). Committing the folder would add a
329
337
  # second copy of the entire tree and a symlink into common-docs.
330
338
  .wt/
339
+ .matrx/fixer-dispatch.json
340
+ .matrx/fixer-ledger.json
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ## 3.1.182
4
+
5
+ - 3.1.181 was tagged against a commit that did not carry the version and never
6
+ published; 3.1.182 is the same code plus everything since.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.174
3
+ Version: 3.1.182
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.50
29
+ Requires-Dist: matrx-utils>=2.0.54
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
@@ -72,7 +72,13 @@ from .core.diagnostics import (
72
72
  long_transactions,
73
73
  long_transactions_sql,
74
74
  )
75
- from .core.introspection import introspect_columns, introspect_rls_policies, schema_exists
75
+ from .core.introspection import (
76
+ introspect_column_shapes,
77
+ introspect_columns,
78
+ introspect_function_privileges,
79
+ introspect_rls_policies,
80
+ schema_exists,
81
+ )
76
82
  from .core.one_database import OneDatabaseReport, PoolIdentity, audit_one_database
77
83
  from .core.listen import notify, ping
78
84
  from .core.tls import resolve_asyncpg_ssl, resolve_asyncpg_ssl_from_dsn
@@ -249,6 +255,7 @@ from .core.registry import (
249
255
  validate_relation_resolution,
250
256
  )
251
257
  from .core.relations import ManyToManyField
258
+ from .core.admin_lane import ADMIN_LANE_GUC, admin_lane
252
259
  from .core.rls_session import rls_session
253
260
  from .core.session_context import (
254
261
  ACTOR_AGENT_GUC,
@@ -472,7 +479,9 @@ __all__ = [
472
479
  "audit_one_database",
473
480
  "OneDatabaseReport",
474
481
  "PoolIdentity",
482
+ "introspect_column_shapes",
475
483
  "introspect_columns",
484
+ "introspect_function_privileges",
476
485
  "introspect_lock_waits",
477
486
  "introspect_rls_policies",
478
487
  "schema_exists",
@@ -709,6 +718,8 @@ __all__ = [
709
718
  "try_advisory_lock",
710
719
  "advisory_unlock",
711
720
  "rls_session",
721
+ "admin_lane",
722
+ "ADMIN_LANE_GUC",
712
723
  # ── Resilience (opt-in transient-failure retry) ──────────────────────────
713
724
  "retry_on_transient",
714
725
  "is_read_only_sql",
@@ -113,3 +113,43 @@ SELECT c.relname AS table_name, a.attname AS column_name
113
113
  AND NOT a.attisdropped
114
114
  AND c.relkind = ANY ('{r,p,v,m,f}')
115
115
  """
116
+
117
+ # The same catalog read, plus the two facts a drift guard actually argues about:
118
+ # whether a column may be empty, and what Postgres puts there when a writer says
119
+ # nothing. A NOT NULL column with a DEFAULT manufactures a value that reads,
120
+ # downstream, exactly like something a person chose — so "is there a default"
121
+ # is a question the ORM must be able to answer without a host hand-rolling SQL.
122
+ COLUMN_SHAPES_FOR_TABLE_SQL = """
123
+ SELECT a.attname AS column_name,
124
+ NOT a.attnotnull AS is_nullable,
125
+ pg_get_expr(d.adbin, d.adrelid) AS column_default
126
+ FROM pg_attribute a
127
+ JOIN pg_class c ON c.oid = a.attrelid
128
+ JOIN pg_namespace n ON n.oid = c.relnamespace
129
+ LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
130
+ WHERE n.nspname = $1
131
+ AND c.relname = $2
132
+ AND a.attnum > 0
133
+ AND NOT a.attisdropped
134
+ AND c.relkind = ANY ('{r,p,v,m,f}')
135
+ """
136
+
137
+
138
+ # "Who may CALL this function?" is the other half of the definer question. A
139
+ # SECURITY DEFINER function runs as the owner with BYPASSRLS, so the set of
140
+ # roles holding EXECUTE on it IS its access control — and a grant that quietly
141
+ # includes `anon` or `authenticated` hands a browser the owner's privileges.
142
+ # Like COLUMN_SHAPES_FOR_TABLE_SQL, this is a catalog question with no Model to
143
+ # bind it to, so the ORM answers it rather than every guard hand-rolling SQL.
144
+ FUNCTION_PRIVILEGES_SQL = """
145
+ SELECT p.oid::regprocedure::text AS signature,
146
+ p.prosecdef AS is_security_definer,
147
+ role_name AS role,
148
+ has_function_privilege(role_name, p.oid, 'EXECUTE') AS can_execute
149
+ FROM pg_proc p
150
+ JOIN pg_namespace n ON n.oid = p.pronamespace
151
+ CROSS JOIN unnest($3::text[]) AS role_name
152
+ WHERE n.nspname = $1
153
+ AND p.proname = $2
154
+ AND EXISTS (SELECT 1 FROM pg_roles r WHERE r.rolname = role_name)
155
+ """
@@ -0,0 +1,47 @@
1
+ """THE ADMIN LANE — the one door that lets a platform admin's database powers work.
2
+
3
+ Our database's admin arms (``is_platform_admin()``, ``is_super_admin()``, ``is_admin()``,
4
+ ``is_super_admin_for(uuid)``, ``is_platform_admin_for(uuid)`` and the kernel's admin rung)
5
+ answer yes only while ``platform.admin_lane_open()`` is true: the request carried the
6
+ admin-app lane header, or the current TRANSACTION set ``matrx.admin_lane`` to ``on``.
7
+ A process on a pooled connection never forwards request headers, so on the server the
8
+ lane is closed unless code opens it here.
9
+
10
+ Law (Arman, 2026-09-25): an admin on a normal user page sees nothing more than anyone
11
+ else; admin powers work only in the admin sections. The lane opens ARMS; it grants
12
+ nothing to a non-admin — the database still checks identity. It is transaction-local,
13
+ so it can never survive onto another client's pooled connection.
14
+
15
+ Only admin surfaces may open it (host guard: ``scripts/check_admin_lane_scope.py``).
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from contextlib import asynccontextmanager
21
+ from typing import AsyncIterator
22
+
23
+ from matrx_orm.core.transaction import get_active_connection, set_local_guc, transaction
24
+
25
+ #: The transaction-local GUC ``platform.admin_lane_open()`` reads. The database names
26
+ #: it; a constant, never configuration — a drift would silently close every lane.
27
+ ADMIN_LANE_GUC = "matrx.admin_lane"
28
+
29
+
30
+ @asynccontextmanager
31
+ async def admin_lane(*, database: str) -> AsyncIterator[None]:
32
+ """Open the admin lane for every database call inside the block.
33
+
34
+ Inside an open transaction (``rls_session`` / ``transaction()``) the lane is switched
35
+ on for THAT transaction; otherwise a transaction is opened so the setting and the
36
+ work share one connection. Either way it resets when the transaction ends.
37
+ """
38
+ if get_active_connection() is not None:
39
+ await set_local_guc(ADMIN_LANE_GUC, "on")
40
+ yield
41
+ return
42
+ async with transaction(database=database):
43
+ await set_local_guc(ADMIN_LANE_GUC, "on")
44
+ yield
45
+
46
+
47
+ __all__ = ["ADMIN_LANE_GUC", "admin_lane"]
@@ -1081,7 +1081,16 @@ def _render_subquery(
1081
1081
  else:
1082
1082
  join_clauses.append(f"{j_type} JOIN {j_table} ON {j_on_sql}")
1083
1083
 
1084
+ # ``from_alias`` is the name every inner column and every nested OuterRef was
1085
+ # just resolved against (``current_table`` above), so the FROM must DECLARE it —
1086
+ # the same contract the top-level executor keeps. Rendering the alias for
1087
+ # resolution but not in FROM produced "missing FROM-clause entry for table
1088
+ # <alias>" on any nested Exists that correlated to an aliased subquery
1089
+ # (search lab inventory, 2026-09-25).
1090
+ from_alias = query_dict.get("from_alias")
1084
1091
  sql = f"SELECT {select_clause} FROM {table}"
1092
+ if from_alias and from_alias != table:
1093
+ sql += f" AS {from_alias}"
1085
1094
  if join_clauses:
1086
1095
  sql += " " + " ".join(join_clauses)
1087
1096
  if where_conditions:
@@ -1596,18 +1596,39 @@ class ForeignKey(Field):
1596
1596
  self.db_type = self._target_field.db_type
1597
1597
  return self._target_field
1598
1598
 
1599
+ def _terminal_field(self) -> Field | None:
1600
+ """The concrete (non-FK) column at the end of this FK's target chain.
1601
+
1602
+ A target column may itself be a ForeignKey, so the chain is followed hop
1603
+ by hop with a visited set. A chain that loops back on itself — a real
1604
+ schema shape: ``content.document.id`` -> ``content.document_version.
1605
+ document_id`` -> ``content.document.id`` — has no concrete column to
1606
+ mirror, so it terminates at ``None`` and every caller takes the same
1607
+ documented UUID fallback an unresolvable target takes. Without the
1608
+ visited set every type read on such a column recursed forever.
1609
+ """
1610
+ visited: set[int] = {id(self)}
1611
+ current: Field = self
1612
+ while isinstance(current, ForeignKey):
1613
+ target = current._get_target_field()
1614
+ if target is None or id(target) in visited:
1615
+ return None
1616
+ visited.add(id(target))
1617
+ current = target
1618
+ return current
1619
+
1599
1620
  def python_type(self) -> type:
1600
- target = self._get_target_field()
1621
+ target = self._terminal_field()
1601
1622
  return target.python_type() if target is not None else str
1602
1623
 
1603
1624
  def to_python(self, value):
1604
- target = self._get_target_field()
1625
+ target = self._terminal_field()
1605
1626
  if target is not None:
1606
1627
  return target.to_python(value)
1607
1628
  return str(value) if value else None
1608
1629
 
1609
1630
  def from_db_value(self, value):
1610
- target = self._get_target_field()
1631
+ target = self._terminal_field()
1611
1632
  if target is not None:
1612
1633
  return target.from_db_value(value)
1613
1634
  return str(value) if value else None
@@ -1616,7 +1637,7 @@ class ForeignKey(Field):
1616
1637
  if value is None:
1617
1638
  return None
1618
1639
  _reject_blank_uuid(self.name, value, "ForeignKey")
1619
- target = self._get_target_field()
1640
+ target = self._terminal_field()
1620
1641
  if target is not None:
1621
1642
  return target.get_db_prep_value(value)
1622
1643
  return str(value)
@@ -1625,7 +1646,7 @@ class ForeignKey(Field):
1625
1646
  await super().validate(value)
1626
1647
  if value is None:
1627
1648
  return
1628
- target = self._get_target_field()
1649
+ target = self._terminal_field()
1629
1650
  if target is None:
1630
1651
  # No resolved target — keep the historical UUID format check.
1631
1652
  try:
@@ -17,11 +17,21 @@ from __future__ import annotations
17
17
 
18
18
  from typing import Any
19
19
 
20
- from matrx_orm.catalog_sql import COLUMNS_FOR_TABLES_SQL, SCHEMA_EXISTS_SQL
20
+ from matrx_orm.catalog_sql import (
21
+ COLUMN_SHAPES_FOR_TABLE_SQL,
22
+ COLUMNS_FOR_TABLES_SQL,
23
+ FUNCTION_PRIVILEGES_SQL,
24
+ SCHEMA_EXISTS_SQL,
25
+ )
21
26
  from matrx_orm.core.async_db_manager import AsyncDatabaseManager
22
27
  from matrx_orm.core.transaction import get_active_connection
23
28
 
24
- __all__ = ["introspect_rls_policies", "introspect_columns", "schema_exists"]
29
+ __all__ = [
30
+ "introspect_function_privileges",
31
+ "introspect_rls_policies",
32
+ "introspect_columns",
33
+ "schema_exists",
34
+ ]
25
35
 
26
36
  _RLS_AUDIT_SQL = """
27
37
  select
@@ -77,6 +87,7 @@ async def introspect_rls_policies(
77
87
  # we assume, present columns read as absent and the drift guard reports the code
78
88
  # as wrong about the schema. See matrx_orm/catalog_sql.py.
79
89
  _COLUMNS_SQL = COLUMNS_FOR_TABLES_SQL
90
+ _COLUMN_SHAPES_SQL = COLUMN_SHAPES_FOR_TABLE_SQL
80
91
 
81
92
 
82
93
  async def introspect_columns(database: str, schema: str, tables: list[str]) -> dict[str, list[str]]:
@@ -120,3 +131,66 @@ async def schema_exists(database: str, schema: str) -> bool:
120
131
 
121
132
  async with AsyncDatabaseManager.get_connection(database) as conn:
122
133
  return bool(await conn.fetchval(sql, schema))
134
+
135
+
136
+ async def introspect_column_shapes(
137
+ database: str, schema: str, table: str
138
+ ) -> dict[str, dict[str, object]]:
139
+ """Live nullability and default per column — ``{column: {...}}``.
140
+
141
+ ``introspect_columns`` answers "does this column exist"; this answers "what
142
+ will Postgres do when a writer says nothing about it". A NOT NULL column
143
+ with a DEFAULT does not leave a hole — it FILLS one, and every reader
144
+ downstream sees a value indistinguishable from a deliberate choice. On
145
+ 2026-09-22 that shape on one column
146
+ (``communication.sms_notification_preferences.timezone``) made an entire
147
+ feature a no-op in production. A guard must be able to see it without
148
+ hand-rolling SQL.
149
+
150
+ Each value is ``{"is_nullable": bool, "column_default": str | None}``.
151
+ ``schema``/``table`` are bound as parameters, never interpolated.
152
+ """
153
+ rows = await AsyncDatabaseManager.execute_query(database, _COLUMN_SHAPES_SQL, schema, table)
154
+ return {
155
+ str(r["column_name"]): {
156
+ "is_nullable": bool(r["is_nullable"]),
157
+ "column_default": (
158
+ str(r["column_default"]) if r["column_default"] is not None else None
159
+ ),
160
+ }
161
+ for r in rows
162
+ }
163
+
164
+
165
+ async def introspect_function_privileges(
166
+ database: str, schema: str, name: str, roles: list[str]
167
+ ) -> list[dict[str, Any]]:
168
+ """Which of ``roles`` can EXECUTE ``schema.name`` — one row per overload/role.
169
+
170
+ ``introspect_column_shapes`` answers "what will Postgres do when a writer
171
+ says nothing"; this answers "who can call this door". A SECURITY DEFINER
172
+ function runs as its owner with BYPASSRLS, so the roles holding EXECUTE on
173
+ it ARE its access control: if ``anon`` or ``authenticated`` is among them,
174
+ a browser holds the owner's privileges. A guard has to be able to see that
175
+ without hand-rolling a catalog query, which is why this lives here.
176
+
177
+ Each row is ``{"signature": str, "is_security_definer": bool, "role": str,
178
+ "can_execute": bool}``. An overload that exists yields one row per role in
179
+ ``roles``; a function that does not exist yields no rows at all, which is
180
+ how a caller tells "missing" from "locked". A name in ``roles`` that is not
181
+ a role on this server is skipped rather than raising — a server without a
182
+ ``supabase_auth_admin`` is a different deployment, not a violation.
183
+ ``schema``, ``name`` and ``roles`` are bound parameters, never interpolated.
184
+ """
185
+ rows = await AsyncDatabaseManager.execute_query(
186
+ database, FUNCTION_PRIVILEGES_SQL, schema, name, list(roles)
187
+ )
188
+ return [
189
+ {
190
+ "signature": str(r["signature"]),
191
+ "is_security_definer": bool(r["is_security_definer"]),
192
+ "role": str(r["role"]),
193
+ "can_execute": bool(r["can_execute"]),
194
+ }
195
+ for r in rows
196
+ ]
@@ -203,6 +203,7 @@ async def emit_contamination(event: EmptyReadContaminationEvent) -> None:
203
203
  ),
204
204
  kind="pooler_role_contamination",
205
205
  source_app="matrx-orm",
206
+ source_feature="system",
206
207
  route=f"orm:{event.config_name}",
207
208
  payload={
208
209
  "config_name": event.config_name,
@@ -51,6 +51,23 @@ _ROLE_RE = re.compile(r"^[a-z_][a-z0-9_]*$")
51
51
  # agent query holding a pooled connection. Callers may override or disable (None).
52
52
  DEFAULT_STATEMENT_TIMEOUT_MS = 30_000
53
53
 
54
+ # Default ceiling on how long an RLS session may sit IDLE INSIDE ITS TRANSACTION before
55
+ # Postgres ends it. Measured on the main database 2026-09-20, lane TIDY: one backend sat
56
+ # `idle in transaction` on `Client/ClientRead` for 89.7 seconds holding AccessShareLock on
57
+ # every partition and index of `custom.record`, `custom.merge_field_provenance` and
58
+ # `custom.anon_form`. A campaign suite then asked for AccessExclusiveLock on one of those
59
+ # relations and queued behind it — and because Postgres's lock queue is FIFO, every
60
+ # ordinary reader that arrived after that queued behind the DDL, four backends deep within
61
+ # seconds, none of which conflicted with the lock actually being held.
62
+ #
63
+ # Nothing legitimate is idle inside a transaction for thirty seconds: the whole point of
64
+ # holding a session open for a turn is that work is happening in it. What IS idle that long
65
+ # is a turn whose caller is waiting on a model, a network hop or a person — and a
66
+ # connection holding store locks while it waits is a stall for everybody else. This ends
67
+ # such a session instead, releasing every lock; the caller sees a named error rather than
68
+ # the rest of the platform seeing a mystery.
69
+ DEFAULT_IDLE_IN_TRANSACTION_TIMEOUT_MS = 30_000
70
+
54
71
 
55
72
  @asynccontextmanager
56
73
  async def rls_session(
@@ -60,6 +77,7 @@ async def rls_session(
60
77
  database: str | None = None,
61
78
  statement_timeout_ms: int | None = DEFAULT_STATEMENT_TIMEOUT_MS,
62
79
  lock_timeout_ms: int | None = None,
80
+ idle_in_transaction_timeout_ms: int | None = DEFAULT_IDLE_IN_TRANSACTION_TIMEOUT_MS,
63
81
  ) -> AsyncIterator[None]:
64
82
  """Run the enclosed block as ``role`` with ``request.jwt.claims`` set to ``claims``.
65
83
 
@@ -72,6 +90,13 @@ async def rls_session(
72
90
  ``statement_timeout`` does not bound it, and an unbounded waiter queues behind the
73
91
  dump or sweep that is already running while blocking everyone behind it. Like the
74
92
  role and the claims it is ``SET LOCAL`` — it cannot outlive the transaction.
93
+
94
+ ``idle_in_transaction_timeout_ms`` bounds how long this session may sit idle INSIDE
95
+ its transaction while holding every lock it has taken. It rides the same one round
96
+ trip as the other three settings, so it is free, and it is what stops one waiting
97
+ caller becoming a stall for every reader of the relations it touched — see
98
+ :data:`DEFAULT_IDLE_IN_TRANSACTION_TIMEOUT_MS` for the 89.7-second case that put it
99
+ here. ``None`` leaves the server default alone.
75
100
  """
76
101
  if not _ROLE_RE.match(role):
77
102
  raise DatabaseError(
@@ -119,6 +144,11 @@ async def rls_session(
119
144
  if lock_timeout_ms is not None:
120
145
  bound.append(str(int(lock_timeout_ms)))
121
146
  settings.append(f"set_config('lock_timeout', ${len(bound)}, true)")
147
+ if idle_in_transaction_timeout_ms is not None:
148
+ bound.append(str(int(idle_in_transaction_timeout_ms)))
149
+ settings.append(
150
+ f"set_config('idle_in_transaction_session_timeout', ${len(bound)}, true)"
151
+ )
122
152
  bound.append(role)
123
153
  settings.append(f"set_config('role', ${len(bound)}, true)")
124
154
  await conn.execute("select " + ", ".join(settings), *bound)
@@ -236,6 +236,21 @@ SELECT
236
236
  WHERE fk.conrelid = a.attrelid
237
237
  AND fk.contype = 'f'
238
238
  AND array_position(fk.conkey, a.attnum) IS NOT NULL
239
+ -- The SAME rule the model generator uses (python_sql/db_objects.py): a
240
+ -- composite FK's column is a reference only when its target column is
241
+ -- unique alone. Diverging would make the diff propose dropping an FK
242
+ -- the models "lost" under a constraint name that does not exist.
243
+ AND (
244
+ cardinality(fk.conkey) = 1
245
+ OR EXISTS (
246
+ SELECT 1 FROM pg_index ri
247
+ WHERE ri.indrelid = fk.confrelid
248
+ AND ri.indisunique AND ri.indpred IS NULL
249
+ AND ri.indnkeyatts = 1
250
+ AND ri.indkey[0] = fk.confkey[array_position(fk.conkey, a.attnum)]
251
+ )
252
+ )
253
+ ORDER BY cardinality(fk.conkey), fk.conname
239
254
  LIMIT 1
240
255
  ) AS fk_ref
241
256
  FROM pg_attribute a
@@ -0,0 +1,190 @@
1
+ """Session-wide guard: a test run never reaches the LIVE platform database by accident.
2
+
3
+ Registered as a ``pytest11`` entry point, so pytest loads it in every suite regardless of
4
+ rootdir — the same mechanism as the spill guard (``matrx_orm.pytest_plugin``) and the lock
5
+ guard (``matrx_orm.pytest_lock_guard``). This is the SEVENTH cross-suite hazard; the other six
6
+ are catalogued in the aidream repo-root ``conftest.py``.
7
+
8
+ WHY IT EXISTS (lane INTEG-SERVER, 2026-09-24). A developer's ``.env`` holds the five
9
+ ``SUPABASE_MATRIX_*`` variables for the LIVE database, because that is what the server runs
10
+ on. Those variables SELECT a connection; they never AUTHORISED one. So every suite that
11
+ imports the host's ``db`` package — ``aidream/services/agent_data/tests/test_rls_data_access.py``
12
+ creates, edits and HARD-deletes notes — ran against production whenever it was collected,
13
+ with nobody having decided that. The matrx-records suite closed this for itself on 2026-09-22
14
+ (a declared target, default the rehearsal branch, main behind three locked doors); this closes
15
+ it for every suite, once, in the package that owns the connection.
16
+
17
+ THE RULE. Before any test module is imported, the connection the five variables (or the
18
+ ``.env`` that will supply them) resolve to is compared with the live database's identity —
19
+ its project ref in the pooler user (``postgres.<ref>``) or a live host. When it IS the live
20
+ database:
21
+
22
+ * **Default:** the five variables are REPOINTED, in this process only, to the rehearsal
23
+ branch — ``MATRX_TEST_DATABASE_URL`` when set (any declared non-live target, e.g. the dev
24
+ clone), else ``SUPABASE_BRANCH_DATABASE_URL``. With neither, they point at an unreachable
25
+ placeholder (``127.0.0.1:1``), so a live test FAILS to connect instead of writing to
26
+ production. The banner says which, once.
27
+ * **Live, on purpose:** ``--live-db --live-db-reason "<why>"`` inside the 1–4 AM Pacific
28
+ maintenance window leaves them alone and prints the reason. The environment cannot open
29
+ this door (an exported variable outlives the shell that set it); a missing reason or a
30
+ time outside the window is a usage error that stops the session.
31
+
32
+ A connection that is NOT the live database — a local Postgres in CI, the branch, the clone —
33
+ is never touched. Values are never printed.
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import os
39
+ from datetime import datetime
40
+ from urllib.parse import unquote, urlsplit
41
+ from zoneinfo import ZoneInfo
42
+
43
+ import pytest
44
+
45
+ #: The ONE live database (aidream CLAUDE.md), by the identities a connection can carry.
46
+ LIVE_PROJECT_REFS = frozenset({"brsgrqvjdzwihsvnfqkf"})
47
+ LIVE_HOSTS = frozenset({"db.matrxserver.com", "db.brsgrqvjdzwihsvnfqkf.supabase.co"})
48
+
49
+ DB_VARS = (
50
+ "SUPABASE_MATRIX_HOST",
51
+ "SUPABASE_MATRIX_PORT",
52
+ "SUPABASE_MATRIX_USER",
53
+ "SUPABASE_MATRIX_PASSWORD",
54
+ "SUPABASE_MATRIX_DATABASE_NAME",
55
+ )
56
+ TARGET_URL_VARS = ("MATRX_TEST_DATABASE_URL", "SUPABASE_BRANCH_DATABASE_URL")
57
+ WINDOW_TZ, WINDOW_START, WINDOW_END = "America/Los_Angeles", 1, 4
58
+ PLACEHOLDER = {
59
+ "SUPABASE_MATRIX_HOST": "127.0.0.1",
60
+ "SUPABASE_MATRIX_PORT": "1",
61
+ "SUPABASE_MATRIX_USER": "live-db-refused-under-pytest",
62
+ "SUPABASE_MATRIX_PASSWORD": "refused",
63
+ "SUPABASE_MATRIX_DATABASE_NAME": "refused",
64
+ }
65
+
66
+ #: The ONLY accounts a test may act as (workspace law: "use ONLY admin@admin.com, never Arman's
67
+ #: personal account"; test@test.com is the non-admin seat). By id, because an id is what a test
68
+ #: reads from ``TEST_USER_ID`` and an id cannot be mistaken for a similar email.
69
+ TEST_ACCOUNT_IDS = {
70
+ "87a6e699-3622-4869-8843-d0867456c0dd": "admin@admin.com",
71
+ "4060701e-706a-4c76-b3ca-0bbc69fa5a14": "test@test.com",
72
+ }
73
+
74
+
75
+ def refuse_a_real_person(env: dict[str, str], dotenv: dict[str, str]) -> None:
76
+ """``TEST_USER_ID`` must be one of the test accounts, or the session does not start."""
77
+ user = (env.get("TEST_USER_ID") or dotenv.get("TEST_USER_ID") or "").strip()
78
+ if user and user not in TEST_ACCOUNT_IDS:
79
+ raise pytest.UsageError(
80
+ "REFUSED: TEST_USER_ID is not a test account. Tests act ONLY as admin@admin.com "
81
+ "(or test@test.com for a non-admin seat) — never a real person's account, and never "
82
+ "the owner's personal one. Set TEST_USER_ID to admin@admin.com's user id. Nothing ran."
83
+ )
84
+
85
+
86
+ #: What this session decided, for tests and banners: "untouched" | "live" | "repointed:<var>"
87
+ #: | "placeholder".
88
+ DECISION: str = "untouched"
89
+
90
+
91
+ def is_live(user: str, host: str) -> bool:
92
+ """Does this connection identity name the live database?"""
93
+ ref = user.split(".", 1)[1] if "." in (user or "") else ""
94
+ return ref in LIVE_PROJECT_REFS or (host or "").strip().lower() in LIVE_HOSTS
95
+
96
+
97
+ def _dotenv_values() -> dict[str, str]:
98
+ """The ``.env`` a later ``load_dotenv()`` would read — it never overrides what is set here."""
99
+ try:
100
+ from dotenv import dotenv_values, find_dotenv
101
+ except ImportError: # pragma: no cover — python-dotenv absent: env only
102
+ return {}
103
+ path = find_dotenv(usecwd=True)
104
+ return {k: v for k, v in (dotenv_values(path) if path else {}).items() if v is not None}
105
+
106
+
107
+ def _five_from_url(url: str) -> dict[str, str]:
108
+ parts = urlsplit(url)
109
+ return {
110
+ "SUPABASE_MATRIX_HOST": parts.hostname or "",
111
+ "SUPABASE_MATRIX_PORT": str(parts.port or 5432),
112
+ "SUPABASE_MATRIX_USER": unquote(parts.username or ""),
113
+ "SUPABASE_MATRIX_PASSWORD": unquote(parts.password or ""),
114
+ "SUPABASE_MATRIX_DATABASE_NAME": (parts.path or "/postgres").lstrip("/") or "postgres",
115
+ }
116
+
117
+
118
+ def decide(
119
+ env: dict[str, str],
120
+ dotenv: dict[str, str],
121
+ *,
122
+ live_flag: bool,
123
+ reason: str,
124
+ now: datetime,
125
+ ) -> tuple[str, dict[str, str] | None, str]:
126
+ """``(decision, five variables to set or None, banner)``. Pure — the hook applies it."""
127
+ effective = {k: env.get(k) or dotenv.get(k, "") for k in DB_VARS}
128
+ if not is_live(effective["SUPABASE_MATRIX_USER"], effective["SUPABASE_MATRIX_HOST"]):
129
+ return "untouched", None, ""
130
+ if live_flag:
131
+ if not reason.strip():
132
+ raise pytest.UsageError(
133
+ "--live-db needs --live-db-reason \"<why>\": nothing else records why a test run "
134
+ "touched the live database."
135
+ )
136
+ local = now.astimezone(ZoneInfo(WINDOW_TZ))
137
+ if not (WINDOW_START <= local.hour < WINDOW_END):
138
+ raise pytest.UsageError(
139
+ f"--live-db refused: it is {local:%H:%M} Pacific, and the live database is open "
140
+ f"to test runs only {WINDOW_START:02d}:00–{WINDOW_END:02d}:00 Pacific."
141
+ )
142
+ return "live", None, f"── LIVE DATABASE under pytest · reason: {reason.strip()}"
143
+ for var in TARGET_URL_VARS:
144
+ url = env.get(var) or dotenv.get(var, "")
145
+ if url:
146
+ five = _five_from_url(url)
147
+ if is_live(five["SUPABASE_MATRIX_USER"], five["SUPABASE_MATRIX_HOST"]):
148
+ raise pytest.UsageError(f"{var} names the LIVE database; it cannot be a test target.")
149
+ return (
150
+ f"repointed:{var}",
151
+ five,
152
+ f"── the live database was configured; this test run uses {var} instead "
153
+ "(pass --live-db --live-db-reason inside 1–4 AM Pacific to reach live on purpose).",
154
+ )
155
+ return (
156
+ "placeholder",
157
+ dict(PLACEHOLDER),
158
+ "── the live database was configured and no test target is set "
159
+ "(MATRX_TEST_DATABASE_URL / SUPABASE_BRANCH_DATABASE_URL); database connections in this "
160
+ "run go to an unreachable placeholder and FAIL instead of reaching production.",
161
+ )
162
+
163
+
164
+ def pytest_addoption(parser: pytest.Parser) -> None:
165
+ group = parser.getgroup("matrx-live-db", "the live platform database under pytest")
166
+ group.addoption("--live-db", action="store_true", default=False,
167
+ help="reach the LIVE database on purpose (needs --live-db-reason and 1-4 AM Pacific)")
168
+ group.addoption("--live-db-reason", action="store", default="",
169
+ help="why this run needs the live database")
170
+
171
+
172
+ @pytest.hookimpl(tryfirst=True)
173
+ def pytest_configure(config: pytest.Config) -> None:
174
+ global DECISION
175
+ refuse_a_real_person(dict(os.environ), _dotenv_values())
176
+ decision, five, banner = decide(
177
+ dict(os.environ),
178
+ _dotenv_values(),
179
+ live_flag=bool(config.getoption("--live-db", default=False)),
180
+ reason=str(config.getoption("--live-db-reason", default="") or ""),
181
+ now=datetime.now(ZoneInfo(WINDOW_TZ)),
182
+ )
183
+ DECISION = decision
184
+ if five is not None:
185
+ os.environ.update(five)
186
+ if banner:
187
+ print(banner)
188
+
189
+
190
+ __all__ = ["DECISION", "TEST_ACCOUNT_IDS", "refuse_a_real_person", "LIVE_HOSTS", "LIVE_PROJECT_REFS", "decide", "is_live"]