matrx-orm 3.1.174__tar.gz → 3.1.176__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 (404) hide show
  1. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/.gitignore +10 -0
  2. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/PKG-INFO +2 -2
  3. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/pooler_role.py +1 -0
  4. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/rls_session.py +30 -0
  5. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/__init__.py +15 -0
  6. matrx_orm-3.1.176/matrx_orm/secrets_battery/backup_restore.py +286 -0
  7. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/mutation_receipts.py +39 -2
  8. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/org_service.py +7 -4
  9. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/README.md +11 -1
  10. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/fallback.py +66 -25
  11. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/lifecycle.py +1 -0
  12. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/managed.py +5 -2
  13. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/session.py +27 -0
  14. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/pyproject.toml +2 -2
  15. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_rls_session_scope.py +58 -7
  16. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_flush_individual_fallback.py +34 -0
  17. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_record_error_bounded_insert.py +9 -1
  18. matrx_orm-3.1.176/tests/test_backup_alias_conflict.py +44 -0
  19. matrx_orm-3.1.176/tests/test_backup_restore_live.py +192 -0
  20. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_passkey_receipts.py +23 -0
  21. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/AGENTS.md +0 -0
  22. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/BASE_CLASS_METHODS.md +0 -0
  23. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/CLAUDE.md +0 -0
  24. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/MODEL_API.md +0 -0
  25. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/README.md +0 -0
  26. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/database/orm/extended/managers/ai_model_base.py +0 -0
  27. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/docs/migrations.md +0 -0
  28. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/FEATURE.md +0 -0
  29. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/MODULE_README.md +0 -0
  30. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/__init__.py +0 -0
  31. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/MODULE_README.md +0 -0
  32. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/__init__.py +0 -0
  33. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/async_postgresql.py +0 -0
  34. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/base_adapter.py +0 -0
  35. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  36. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/adapters/supabase_adapter.py +0 -0
  37. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/admin/FEATURE.md +0 -0
  38. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/admin/__init__.py +0 -0
  39. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/admin/router.py +0 -0
  40. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/__init__.py +0 -0
  41. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/auth.py +0 -0
  42. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/config.py +0 -0
  43. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/handlers.py +0 -0
  44. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/protocol.py +0 -0
  45. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/api/server.py +0 -0
  46. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/associations.py +0 -0
  47. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/cache_debug.py +0 -0
  48. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/catalog.py +0 -0
  49. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/catalog_sql.py +0 -0
  50. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/MODULE_README.md +0 -0
  51. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/__init__.py +0 -0
  52. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/postgres_connection.py +0 -0
  53. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/postgrest.py +0 -0
  54. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/supabase_auth.py +0 -0
  55. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/supabase_config.py +0 -0
  56. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/client/supabase_manager.py +0 -0
  57. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  58. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/FEATURE.md +0 -0
  59. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/MODULE_README.md +0 -0
  60. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  61. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/TASKS.md +0 -0
  62. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/__init__.py +0 -0
  63. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/async_db_manager.py +0 -0
  64. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/base.py +0 -0
  65. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/config.py +0 -0
  66. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/connections.py +0 -0
  67. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/db_function.py +0 -0
  68. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/diagnostics.py +0 -0
  69. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/expressions.py +0 -0
  70. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/extended.py +0 -0
  71. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/fields.py +0 -0
  72. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/introspection.py +0 -0
  73. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/listen.py +0 -0
  74. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/loop_filters.py +0 -0
  75. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/model_dto.py +0 -0
  76. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/model_view.py +0 -0
  77. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/one_database.py +0 -0
  78. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/paginator.py +0 -0
  79. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/pool_maintenance.py +0 -0
  80. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/pool_watch.py +0 -0
  81. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/pydantic_bridge.py +0 -0
  82. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/registry.py +0 -0
  83. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/relations.py +0 -0
  84. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/resilience.py +0 -0
  85. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/session_context.py +0 -0
  86. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/signals.py +0 -0
  87. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/tls.py +0 -0
  88. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/transaction.py +0 -0
  89. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/types.py +0 -0
  90. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/write_queue.py +0 -0
  91. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/core/write_retry.py +0 -0
  92. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/entity.py +0 -0
  93. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/error_handling.py +0 -0
  94. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/exceptions.py +0 -0
  95. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/extended/__init__.py +0 -0
  96. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/extended/app_error_handler.py +0 -0
  97. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/identity_rule.py +0 -0
  98. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/local_sqlite.py +0 -0
  99. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/middleware/__init__.py +0 -0
  100. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/middleware/base.py +0 -0
  101. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/MODULE_README.md +0 -0
  102. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/__init__.py +0 -0
  103. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/cli.py +0 -0
  104. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/ddl.py +0 -0
  105. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/diff.py +0 -0
  106. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/executor.py +0 -0
  107. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/integration.py +0 -0
  108. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/loader.py +0 -0
  109. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/operations.py +0 -0
  110. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/state.py +0 -0
  111. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/migrations/table_filter.py +0 -0
  112. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/no_org_assignment.py +0 -0
  113. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/FEATURE.md +0 -0
  114. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/MODULE_README.md +0 -0
  115. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/__init__.py +0 -0
  116. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/bulk_update_values.py +0 -0
  117. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/catalog_select.py +0 -0
  118. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/conflict.py +0 -0
  119. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/conflict_writes.py +0 -0
  120. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/create.py +0 -0
  121. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/db_functions.py +0 -0
  122. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/delete.py +0 -0
  123. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/dynamic_admin.py +0 -0
  124. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/dynamic_crud.py +0 -0
  125. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/graph_walk.py +0 -0
  126. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/latest_rows.py +0 -0
  127. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/queue_claim.py +0 -0
  128. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/read.py +0 -0
  129. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/relational_backfill.py +0 -0
  130. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/staging_load.py +0 -0
  131. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/operations/update.py +0 -0
  132. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/platform_access.py +0 -0
  133. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/platform_db.py +0 -0
  134. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/py.typed +0 -0
  135. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/pytest_lock_guard.py +0 -0
  136. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/pytest_plugin.py +0 -0
  137. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/MODULE_README.md +0 -0
  138. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/__init__.py +0 -0
  139. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/db_objects.py +0 -0
  140. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  141. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  142. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/FEATURE.md +0 -0
  143. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/MODULE_README.md +0 -0
  144. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/__init__.py +0 -0
  145. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/builder.py +0 -0
  146. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/query/executor.py +0 -0
  147. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/resources/__init__.py +0 -0
  148. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
  149. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
  150. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/FEATURE.md +0 -0
  151. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  152. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/__init__.py +0 -0
  153. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/code_handler.py +0 -0
  154. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  155. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/columns.py +0 -0
  156. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/common.py +0 -0
  157. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/diff_preview.py +0 -0
  158. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  159. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/generator.py +0 -0
  160. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  161. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  162. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  163. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  164. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/package_wiring.py +0 -0
  165. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/relationships.py +0 -0
  166. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/runner.py +0 -0
  167. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/schema.py +0 -0
  168. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/schema_manager.py +0 -0
  169. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/tables.py +0 -0
  170. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/schema_builder/views.py +0 -0
  171. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/FEATURE.md +0 -0
  172. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/attachments.py +0 -0
  173. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/crypto.py +0 -0
  174. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  175. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/items.py +0 -0
  176. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/model.py +0 -0
  177. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/native_enable.py +0 -0
  178. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
  179. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
  180. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/native_source.py +0 -0
  181. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/secrets_battery/service.py +0 -0
  182. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/__init__.py +0 -0
  183. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/coalesce.py +0 -0
  184. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/dag.py +0 -0
  185. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/errors.py +0 -0
  186. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/flush.py +0 -0
  187. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/op.py +0 -0
  188. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/reads.py +0 -0
  189. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/session/telemetry.py +0 -0
  190. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  191. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/__init__.py +0 -0
  192. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/executor.py +0 -0
  193. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/queries.py +0 -0
  194. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/registry.py +0 -0
  195. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/types.py +0 -0
  196. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/sql_executor/utils.py +0 -0
  197. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/state.py +0 -0
  198. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/utils/__init__.py +0 -0
  199. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/utils/sql_utils.py +0 -0
  200. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/utils/type_converters.py +0 -0
  201. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/matrx_orm/write_actor.py +0 -0
  202. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/scripts/git-branches.sh +0 -0
  203. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/scripts/publish.sh +0 -0
  204. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/scripts/release.sh +0 -0
  205. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/MODULE_README.md +0 -0
  206. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/__init__.py +0 -0
  207. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/conftest.py +0 -0
  208. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/MODULE_README.md +0 -0
  209. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/__init__.py +0 -0
  210. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_adapter_registry.py +0 -0
  211. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_admin_db_columns.py +0 -0
  212. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_admin_response_rendering.py +0 -0
  213. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_admin_search_negation.py +0 -0
  214. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_admin_search_sentinels.py +0 -0
  215. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_advisory_lock.py +0 -0
  216. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_agent_message.py +0 -0
  217. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_api_auth.py +0 -0
  218. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_api_config.py +0 -0
  219. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_api_handlers.py +0 -0
  220. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_api_protocol.py +0 -0
  221. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_array_agg_order_by.py +0 -0
  222. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_association_trigger_actor_naming.py +0 -0
  223. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_associations.py +0 -0
  224. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_associations_wiring.py +0 -0
  225. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_bulk_hydration_offload.py +0 -0
  226. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_bulk_update_by_pk.py +0 -0
  227. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  228. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_cache_debug.py +0 -0
  229. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_call_function.py +0 -0
  230. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_case_when.py +0 -0
  231. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_catalog.py +0 -0
  232. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_committed_row_guard.py +0 -0
  233. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_composite_pk_filter.py +0 -0
  234. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_composite_pk_write_paths.py +0 -0
  235. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_config.py +0 -0
  236. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_conflict_target.py +0 -0
  237. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_conflict_writes.py +0 -0
  238. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_connection_codecs.py +0 -0
  239. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_connection_poison_guard.py +0 -0
  240. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_connection_primitives.py +0 -0
  241. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_count_composite_distinct.py +0 -0
  242. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_count_limit_param_leak.py +0 -0
  243. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_db_function_field.py +0 -0
  244. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_ddl_generator.py +0 -0
  245. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_deadlock_retry.py +0 -0
  246. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_derived_table.py +0 -0
  247. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_dirty_tracking.py +0 -0
  248. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_dynamic_crud.py +0 -0
  249. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_exceptions.py +0 -0
  250. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  251. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_expression_primitives.py +0 -0
  252. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_fields.py +0 -0
  253. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  254. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_from_alias.py +0 -0
  255. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_fts.py +0 -0
  256. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_func_expression_args.py +0 -0
  257. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_graph_walk.py +0 -0
  258. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_having_annotate_alias.py +0 -0
  259. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  260. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_introspect_rls_policies.py +0 -0
  261. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_join_and_aggregate.py +0 -0
  262. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_latest_rows.py +0 -0
  263. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_listen_notify.py +0 -0
  264. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_local_sqlite.py +0 -0
  265. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_lock_contention_retry.py +0 -0
  266. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_managed_write_guard.py +0 -0
  267. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_matrx_entity.py +0 -0
  268. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_migration_diff_types.py +0 -0
  269. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_migration_loader.py +0 -0
  270. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_model_instance.py +0 -0
  271. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_model_meta.py +0 -0
  272. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_optimistic_concurrency.py +0 -0
  273. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_order_by_expression.py +0 -0
  274. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_org_assignment_inventory.py +0 -0
  275. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
  276. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_parameter_name_hydration.py +0 -0
  277. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_patch_jsonb_path.py +0 -0
  278. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_percentile_cont.py +0 -0
  279. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_persisted_count.py +0 -0
  280. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_pool_holder_census.py +0 -0
  281. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_pool_loop_recreation.py +0 -0
  282. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_pool_maintenance.py +0 -0
  283. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_postgrest_filters.py +0 -0
  284. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_pydantic_bridge.py +0 -0
  285. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_query_builder.py +0 -0
  286. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_query_error_detail.py +0 -0
  287. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_query_executor_sql.py +0 -0
  288. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_query_timeout.py +0 -0
  289. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  290. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_queue_claim.py +0 -0
  291. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_read_connection_retry.py +0 -0
  292. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_registry.py +0 -0
  293. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_registry_multi_database.py +0 -0
  294. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_registry_multi_schema.py +0 -0
  295. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_relational_backfill.py +0 -0
  296. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_relations.py +0 -0
  297. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_resilience.py +0 -0
  298. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_schema_exists.py +0 -0
  299. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_session_advisory_lock.py +0 -0
  300. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_standalone_write_session_context.py +0 -0
  301. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_state_cache.py +0 -0
  302. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_subquery_filter_raw.py +0 -0
  303. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_subquery_in_filter.py +0 -0
  304. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_subquery_nested_expressions.py +0 -0
  305. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_supabase_auth.py +0 -0
  306. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_supabase_config.py +0 -0
  307. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_tls.py +0 -0
  308. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_transaction_database_identity.py +0 -0
  309. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_typed_join_projection.py +0 -0
  310. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_typed_predicates.py +0 -0
  311. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_update_case_expression.py +0 -0
  312. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_update_subquery.py +0 -0
  313. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_update_with_rebase.py +0 -0
  314. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_upsert_default_leak.py +0 -0
  315. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_upsert_with_conflict.py +0 -0
  316. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_values_jsonb_decode.py +0 -0
  317. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_watched_lifecycle.py +0 -0
  318. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_write_queue_deadline.py +0 -0
  319. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level1/test_write_retry_semantics.py +0 -0
  320. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/MODULE_README.md +0 -0
  321. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/__init__.py +0 -0
  322. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/conftest.py +0 -0
  323. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_bulk_ops.py +0 -0
  324. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_cache_integration.py +0 -0
  325. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_crud.py +0 -0
  326. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_foreign_keys.py +0 -0
  327. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_m2m.py +0 -0
  328. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_manager.py +0 -0
  329. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_migrations_live.py +0 -0
  330. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_query_execution.py +0 -0
  331. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/test_schema_diff.py +0 -0
  332. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
  333. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/live_pooler_role.py +0 -0
  334. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/native_source_cases.py +0 -0
  335. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/native_source_oracle.rs +0 -0
  336. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/.env.example +0 -0
  337. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/README.md +0 -0
  338. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/__init__.py +0 -0
  339. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/generate.py +0 -0
  340. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/generated/.gitkeep +0 -0
  341. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/matrx_orm.yaml +0 -0
  342. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project/test_schema_generation.py +0 -0
  343. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/.env.example +0 -0
  344. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/README.md +0 -0
  345. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/__init__.py +0 -0
  346. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/client_example.py +0 -0
  347. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/client_example.ts +0 -0
  348. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  349. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/sample_project_desktop/server.py +0 -0
  350. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/entity_tests.py +0 -0
  351. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_base_generation.py +0 -0
  352. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  353. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_composite_unique_generation.py +0 -0
  354. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_dto_identity_generation.py +0 -0
  355. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_entity_capability_generation.py +0 -0
  356. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_enum_ordering.py +0 -0
  357. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_generate_schema.py +0 -0
  358. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_junction_analysis.py +0 -0
  359. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_multi_schema_output.py +0 -0
  360. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_schema_analysis.py +0 -0
  361. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_sql_expression_defaults.py +0 -0
  362. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_table_relationships_query.py +0 -0
  363. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema/test_view_generation.py +0 -0
  364. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema_builder/test_array_default_parsing.py +0 -0
  365. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  366. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  367. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/__init__.py +0 -0
  368. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/conftest.py +0 -0
  369. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_cache_no_downgrade.py +0 -0
  370. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_capture_poison_proof.py +0 -0
  371. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_coalesce.py +0 -0
  372. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_dag.py +0 -0
  373. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_disk_spill_fallback.py +0 -0
  374. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_governed_write.py +0 -0
  375. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_managed.py +0 -0
  376. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_op_enqueue_site.py +0 -0
  377. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_reads.py +0 -0
  378. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_record_error_storm.py +0 -0
  379. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_session.py +0 -0
  380. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/session/test_spill_guard_plugin.py +0 -0
  381. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_array_arg_element_type.py +0 -0
  382. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_attachment_mutation_serialization.py +0 -0
  383. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_database_name_alias.py +0 -0
  384. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_db_functions.py +0 -0
  385. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_dynamic_select_unregistered.py +0 -0
  386. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_immutable_insert_or_exact_verify.py +0 -0
  387. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_keyed_digest.py +0 -0
  388. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_model_cls_refactor.py +0 -0
  389. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_enable.py +0 -0
  390. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_lifecycle.py +0 -0
  391. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_lifecycle_fences.py +0 -0
  392. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_native_source.py +0 -0
  393. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  394. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_one_database.py +0 -0
  395. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_order_by_desc_nulls_last.py +0 -0
  396. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_pooler_role_guard.py +0 -0
  397. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_secret_audit_names_its_organization.py +0 -0
  398. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_secrets_battery.py +0 -0
  399. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_secrets_minting_keys.py +0 -0
  400. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_shared_lock_guard.py +0 -0
  401. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_sql_param_casts.py +0 -0
  402. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_sync_twins_guard.py +0 -0
  403. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/tests/test_user_visible_lookup.py +0 -0
  404. {matrx_orm-3.1.174 → matrx_orm-3.1.176}/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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.174
3
+ Version: 3.1.176
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.51
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
@@ -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)
@@ -167,6 +167,15 @@ from .service import (
167
167
  )
168
168
 
169
169
 
170
+ from .backup_restore import (
171
+ BackupAttachmentSpec,
172
+ BackupRestoreAliasConflictError,
173
+ BackupRestoreResult,
174
+ create_backup_record_quarantined,
175
+ )
176
+ from .mutation_receipts import complete_backup_restore_receipt, reserve_backup_restore_receipt
177
+
178
+
170
179
  def configure_secrets(
171
180
  *,
172
181
  db_config_name: str | None = None,
@@ -192,6 +201,12 @@ def configure_secrets(
192
201
 
193
202
 
194
203
  __all__ = [
204
+ "BackupAttachmentSpec",
205
+ "BackupRestoreResult",
206
+ "BackupRestoreAliasConflictError",
207
+ "create_backup_record_quarantined",
208
+ "complete_backup_restore_receipt",
209
+ "reserve_backup_restore_receipt",
195
210
  "configure_secrets",
196
211
  "configure_encryption",
197
212
  "fernet_for_key",
@@ -0,0 +1,286 @@
1
+ """Atomic personal backup restoration with execution disabled from insertion."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import AsyncIterator
6
+ from contextlib import asynccontextmanager
7
+ from dataclasses import dataclass
8
+ from typing import Literal
9
+ from uuid import UUID
10
+
11
+ from matrx_orm import transaction
12
+ from matrx_orm.exceptions import describe_db_exception
13
+
14
+ from .attachments import _validate_content, _validate_text
15
+ from .crypto import encrypt_bytes, encrypt_value, mask_value
16
+ from .items import (
17
+ NON_TRANSFERABLE_FIELD_KEYS,
18
+ FieldSpec,
19
+ _metadata_kwargs,
20
+ _refuse_reserved_generic_definition,
21
+ _refuse_reserved_generic_field,
22
+ _validate_field_key,
23
+ )
24
+ from .model import (
25
+ PACKAGE_DB_NAME,
26
+ CredentialAttachment,
27
+ CredentialItem,
28
+ PasskeyCredential,
29
+ UserSecret,
30
+ UserSecretGrant,
31
+ )
32
+ from .mutation_receipts import (
33
+ complete_backup_restore_receipt,
34
+ reserve_backup_restore_receipt,
35
+ )
36
+ from .org_service import record_secret_audit
37
+
38
+
39
+ @dataclass(frozen=True, repr=False)
40
+ class BackupAttachmentSpec:
41
+ """Portable file content; never rendered into logs or exception messages."""
42
+
43
+ label: str
44
+ description: str | None
45
+ file_name: str
46
+ media_type: str
47
+ content: bytes
48
+ source_handling: Literal["visible", "revealable"]
49
+
50
+
51
+ @dataclass(frozen=True)
52
+ class BackupRestoreResult:
53
+ item_id: str
54
+ replayed: bool
55
+
56
+
57
+ class BackupRestoreAliasConflictError(Exception):
58
+ """A restored alias is reserved; the entire record was rolled back."""
59
+
60
+
61
+ @asynccontextmanager
62
+ async def _restore_transaction() -> AsyncIterator[None]:
63
+ try:
64
+ async with transaction(PACKAGE_DB_NAME):
65
+ yield
66
+ except Exception as error:
67
+ # Translate only after rollback; never expose value-bearing DB diagnostics.
68
+ info = describe_db_exception(error)
69
+ if info.sqlstate == "23505" and info.constraint == "user_secrets_unique_personal_key":
70
+ raise BackupRestoreAliasConflictError(
71
+ "An environment alias from this backup is already in use."
72
+ ) from None
73
+ raise
74
+
75
+
76
+ async def create_backup_record_quarantined(
77
+ *,
78
+ actor_id: str,
79
+ audit_organization_id: str,
80
+ mutation_id: str,
81
+ request_fingerprint: bytes,
82
+ display_name: str,
83
+ definition_key: str,
84
+ definition_version: int,
85
+ provider_key: str | None,
86
+ description: str | None,
87
+ tags: list[str],
88
+ login_urls: list[str],
89
+ uri_match_mode: str | None,
90
+ notes: str | None,
91
+ non_secret_fields: list[dict[str, str]],
92
+ fields: list[FieldSpec],
93
+ attachments: list[BackupAttachmentSpec],
94
+ ) -> BackupRestoreResult:
95
+ """Restore one host-authorized personal aggregate and receipt in one transaction."""
96
+ # The host supplies verified actor/context and computes the binding fingerprint.
97
+ # No destination owner, access grant, lifecycle or execution flag is accepted.
98
+ try:
99
+ actor_id = str(UUID(actor_id))
100
+ audit_organization_id = str(UUID(audit_organization_id))
101
+ mutation_id = str(UUID(mutation_id))
102
+ if not isinstance(display_name, str) or not display_name:
103
+ raise ValueError("backup display name is required")
104
+ if type(definition_version) is not int or definition_version <= 0:
105
+ raise ValueError("invalid backup definition version")
106
+ _refuse_reserved_generic_definition(definition_key)
107
+ metadata = _metadata_kwargs(
108
+ login_urls=login_urls,
109
+ uri_match_mode=uri_match_mode,
110
+ notes=notes,
111
+ non_secret_fields=non_secret_fields,
112
+ browser_fill_enabled=False,
113
+ )
114
+ # Validation is shared, but restoration must not silently normalize source text.
115
+ metadata["login_urls"] = login_urls
116
+ seen: set[str] = set()
117
+ for field in fields:
118
+ _validate_field_key(field.field_key)
119
+ _refuse_reserved_generic_field(field.field_key)
120
+ if field.field_key in NON_TRANSFERABLE_FIELD_KEYS or field.field_key in seen:
121
+ raise ValueError("backup contains an unsupported or duplicate field")
122
+ if field.handling not in {"visible", "revealable"} or not isinstance(field.value, str):
123
+ raise ValueError("backup field is not portable")
124
+ if field.inject_into_sandbox:
125
+ raise ValueError("backup cannot enable sandbox injection")
126
+ seen.add(field.field_key)
127
+ for attachment in attachments:
128
+ if attachment.source_handling not in {"visible", "revealable"}:
129
+ raise ValueError("backup attachment is not portable")
130
+ _validate_content(attachment.content)
131
+ _validate_text(attachment.label, name="attachment label", maximum=200)
132
+ _validate_text(attachment.file_name, name="attachment filename", maximum=512)
133
+ _validate_text(attachment.media_type, name="attachment media type", maximum=255)
134
+ if attachment.description is not None and len(attachment.description) > 2000:
135
+ raise ValueError("attachment description is too long")
136
+ except (TypeError, ValueError, AttributeError):
137
+ raise ValueError("Invalid backup restore record.") from None
138
+
139
+ async with _restore_transaction():
140
+ receipt, replayed = await reserve_backup_restore_receipt(
141
+ actor_id=actor_id,
142
+ organization_id=audit_organization_id,
143
+ mutation_id=mutation_id,
144
+ request_fingerprint=request_fingerprint,
145
+ )
146
+ if replayed:
147
+ if receipt.completed_at is None or receipt.result_item_id is None:
148
+ raise RuntimeError("backup restore receipt is incomplete")
149
+ await _validate_replay(receipt.result_item_id, actor_id)
150
+ return BackupRestoreResult(receipt.result_item_id, True)
151
+ item = await CredentialItem.create(
152
+ user_id=actor_id,
153
+ organization_id=None,
154
+ definition_key=definition_key,
155
+ definition_version=definition_version,
156
+ provider_key=provider_key,
157
+ display_name=display_name,
158
+ description=description,
159
+ tags=tags,
160
+ status="disabled",
161
+ source="migration",
162
+ access_mode="restricted",
163
+ lifecycle={},
164
+ **metadata,
165
+ created_by=actor_id,
166
+ updated_by=actor_id,
167
+ )
168
+ for field in fields:
169
+ await UserSecret.create(
170
+ user_id=actor_id,
171
+ organization_id=None,
172
+ credential_item_id=item.id,
173
+ field_key=field.field_key,
174
+ key=field.env_key,
175
+ value_encrypted=encrypt_value(field.value),
176
+ value_hint=mask_value(field.value),
177
+ handling=field.handling,
178
+ editable=field.editable,
179
+ is_active=False,
180
+ inject_into_sandbox=False,
181
+ execution_purpose="general",
182
+ description=field.description,
183
+ access_mode="restricted",
184
+ created_by=actor_id,
185
+ updated_by=actor_id,
186
+ )
187
+ for attachment in attachments:
188
+ await CredentialAttachment.create(
189
+ credential_item_id=item.id,
190
+ label=attachment.label,
191
+ description=attachment.description,
192
+ file_name=attachment.file_name,
193
+ media_type=attachment.media_type,
194
+ size_bytes=len(attachment.content),
195
+ value_encrypted=encrypt_bytes(attachment.content),
196
+ handling=attachment.source_handling,
197
+ created_by=actor_id,
198
+ updated_by=actor_id,
199
+ )
200
+ await record_secret_audit(
201
+ action="item_created",
202
+ fail_closed=True,
203
+ actor_id=actor_id,
204
+ organization_id=audit_organization_id,
205
+ user_id=actor_id,
206
+ metadata={
207
+ "credential_item_id": str(item.id),
208
+ "source": "backup_restore",
209
+ "field_count": len(fields),
210
+ "attachment_count": len(attachments),
211
+ "quarantined": True,
212
+ },
213
+ )
214
+ await complete_backup_restore_receipt(receipt.id, result_item_id=str(item.id))
215
+ return BackupRestoreResult(str(item.id), False)
216
+
217
+
218
+ async def _validate_replay(item_id: str, actor_id: str) -> None:
219
+ rows = (
220
+ await CredentialItem.filter(id=item_id, deleted_at__isnull=True)
221
+ .select_for_update()
222
+ .values(
223
+ "user_id",
224
+ "organization_id",
225
+ "status",
226
+ "access_mode",
227
+ "browser_fill_enabled",
228
+ "definition_key",
229
+ )
230
+ )
231
+ valid = bool(rows)
232
+ if rows:
233
+ item = rows[0]
234
+ valid = (
235
+ str(item["user_id"]) == actor_id
236
+ and item["organization_id"] is None
237
+ and item["status"] == "disabled"
238
+ and item["access_mode"] == "restricted"
239
+ and item["browser_fill_enabled"] is False
240
+ and item["definition_key"] not in {"native_passkey", "passkey_private"}
241
+ )
242
+ fields = await UserSecret.filter(credential_item_id=item_id).values(
243
+ "id",
244
+ "user_id",
245
+ "organization_id",
246
+ "deleted_at",
247
+ "is_active",
248
+ "access_mode",
249
+ "inject_into_sandbox",
250
+ "execution_purpose",
251
+ "handling",
252
+ "field_key",
253
+ )
254
+ for field in fields:
255
+ if field["deleted_at"] is None:
256
+ valid = valid and (
257
+ str(field["user_id"]) == actor_id
258
+ and field["organization_id"] is None
259
+ and field["is_active"] is False
260
+ and field["access_mode"] == "restricted"
261
+ and field["inject_into_sandbox"] is False
262
+ and field["execution_purpose"] == "general"
263
+ and field["handling"] in {"visible", "revealable"}
264
+ and field["field_key"] not in NON_TRANSFERABLE_FIELD_KEYS | {"passkey_private"}
265
+ )
266
+ granted = await UserSecretGrant.filter(credential_item_id=item_id).exists()
267
+ if fields:
268
+ granted = (
269
+ granted
270
+ or await UserSecretGrant.filter(user_secret_id__in=[f["id"] for f in fields]).exists()
271
+ )
272
+ protected = await PasskeyCredential.filter(
273
+ credential_item_id=item_id, deleted_at__isnull=True
274
+ ).exists()
275
+ files = await CredentialAttachment.filter(
276
+ credential_item_id=item_id, deleted_at__isnull=True
277
+ ).values("handling")
278
+ if (
279
+ not valid
280
+ or granted
281
+ or protected
282
+ or any(f["handling"] not in {"visible", "revealable"} for f in files)
283
+ ):
284
+ raise PermissionError(
285
+ "Restored credential has changed; retry cannot confirm its quarantine."
286
+ )
@@ -15,7 +15,10 @@ from matrx_orm import transaction
15
15
 
16
16
  from .model import PACKAGE_DB_NAME, CredentialMutationReceipt
17
17
 
18
- MutationOperation = Literal["create_item", "add_field", "update_field", "native_passkey_create"]
18
+ MutationOperation = Literal[
19
+ "create_item", "add_field", "update_field", "native_passkey_create", "native_passkey_import",
20
+ "backup_restore_item",
21
+ ]
19
22
  PrincipalKind = Literal["user", "organization"]
20
23
 
21
24
 
@@ -242,6 +245,7 @@ async def reserve_native_passkey_receipt(
242
245
  principal_type: PrincipalKind,
243
246
  principal_id: str,
244
247
  request_fingerprint: bytes,
248
+ operation: Literal["native_passkey_create", "native_passkey_import"] = "native_passkey_create",
245
249
  ) -> tuple[MutationReceipt, bool]:
246
250
  """Reserve the native-only receipt shape without exposing generic targets.
247
251
 
@@ -255,7 +259,7 @@ async def reserve_native_passkey_receipt(
255
259
  actor_id=actor_id,
256
260
  organization_id=organization_id,
257
261
  mutation_id=mutation_id,
258
- operation="native_passkey_create",
262
+ operation=operation,
259
263
  principal_type=principal_type,
260
264
  principal_id=principal_id,
261
265
  target_item_id=None,
@@ -279,3 +283,36 @@ async def complete_native_passkey_receipt(
279
283
  result_value_version=1,
280
284
  result_passkey_id=result_passkey_id,
281
285
  )
286
+
287
+
288
+ async def reserve_backup_restore_receipt(
289
+ *,
290
+ actor_id: str,
291
+ organization_id: str,
292
+ mutation_id: str,
293
+ request_fingerprint: bytes,
294
+ ) -> tuple[MutationReceipt, bool]:
295
+ """Bind a personal restore retry to a server-computed encrypted-envelope digest."""
296
+ if not isinstance(request_fingerprint, bytes) or len(request_fingerprint) != 32:
297
+ raise ValueError("backup restore fingerprint must be 32 bytes")
298
+ return await reserve_mutation_receipt(
299
+ actor_id=actor_id,
300
+ organization_id=organization_id,
301
+ mutation_id=mutation_id,
302
+ operation="backup_restore_item",
303
+ principal_type="user",
304
+ principal_id=actor_id,
305
+ target_item_id=None,
306
+ target_field_id=None,
307
+ request_fingerprint=request_fingerprint,
308
+ )
309
+
310
+
311
+ async def complete_backup_restore_receipt(receipt_id: str, *, result_item_id: str) -> None:
312
+ """Complete the item-only result inside the aggregate restore transaction."""
313
+ await complete_mutation_receipt(
314
+ receipt_id,
315
+ result_item_id=result_item_id,
316
+ result_field_id=None,
317
+ result_value_version=None,
318
+ )
@@ -200,12 +200,13 @@ async def record_secret_audit(
200
200
  user_secret_id: str | None = None,
201
201
  user_id: str | None = None,
202
202
  metadata: dict[str, Any] | None = None,
203
+ fail_closed: bool = False,
203
204
  ) -> None:
204
205
  """Append one audit row. Records the ACTION and metadata — NEVER a value.
205
206
 
206
- Audit is a trail, not a transaction: a failed audit write is logged loudly
207
- but never propagates, because losing the trail must not also lose the
208
- operation the user asked for.
207
+ By default audit failures are logged without failing the operation.
208
+ Security-sensitive aggregate writes pass fail_closed=True so a failed
209
+ audit aborts their outer transaction with a value-free error.
209
210
 
210
211
  ``organization_id`` is REQUIRED and refused when empty. ``users
211
212
  .user_secret_audit.organization_id`` has been NOT NULL since migration 0444,
@@ -242,7 +243,9 @@ async def record_secret_audit(
242
243
  action=action,
243
244
  metadata=metadata or {},
244
245
  )
245
- except Exception as exc: # noqa: BLE001 — trail loss must not fail the op
246
+ except Exception as exc: # noqa: BLE001 — explicit fail-closed or best-effort policy
247
+ if fail_closed:
248
+ raise RuntimeError("required credential audit failed") from None
246
249
  logger.error(
247
250
  "secrets audit write FAILED (action=%s secret=%s org=%s): %s",
248
251
  action,
@@ -223,7 +223,17 @@ behavior with structurally-correct one-shot writes.
223
223
 
224
224
  ## Fallback / replay
225
225
 
226
- When `Session.flush()` raises, every dropped op is captured to
226
+ For an all-or-nothing copy, construct `Session(recover_individually=False)`
227
+ through an atomic Coordinator. A batch failure then rolls back the whole
228
+ transaction and reports the original cause without individual write recovery;
229
+ the enclosing operation captures the failure. `Session.abort()` discards
230
+ queued rows when the copy body fails before flush.
231
+
232
+ Error-capture rows use the caller's explicit organization. When no organization
233
+ is available, they use the operator SYSTEM organization on their fresh
234
+ connection; a user ID alone cannot supply the missing actor context there.
235
+
236
+ When a regular `Session.flush()` raises, every dropped op is captured to
227
237
  `ops.system_write_failure` on a fresh asyncpg connection (bypasses
228
238
  the failed transaction's pool slot). The recovery tool
229
239
  (`scripts/replay_write_failures.py` in aidream) can replay them