matrx-orm 3.1.182__tar.gz → 3.1.183__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 (415) hide show
  1. matrx_orm-3.1.183/CHANGELOG.md +17 -0
  2. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/PKG-INFO +1 -1
  3. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/FEATURE.md +15 -0
  4. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/async_db_manager.py +66 -1
  5. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/config.py +9 -0
  6. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/FEATURE.md +12 -2
  7. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/items.py +17 -13
  8. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/vault_history.py +14 -1
  9. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/pyproject.toml +1 -1
  10. matrx_orm-3.1.183/tests/level1/test_pool_create_never_wedges.py +194 -0
  11. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_secrets_battery.py +99 -3
  12. matrx_orm-3.1.182/CHANGELOG.md +0 -6
  13. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/.gitignore +0 -0
  14. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/AGENTS.md +0 -0
  15. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/BASE_CLASS_METHODS.md +0 -0
  16. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/CLAUDE.md +0 -0
  17. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/MODEL_API.md +0 -0
  18. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/README.md +0 -0
  19. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/database/orm/extended/managers/ai_model_base.py +0 -0
  20. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/docs/migrations.md +0 -0
  21. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/FEATURE.md +0 -0
  22. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/MODULE_README.md +0 -0
  23. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/__init__.py +0 -0
  24. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/adapters/MODULE_README.md +0 -0
  25. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/adapters/__init__.py +0 -0
  26. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/adapters/async_postgresql.py +0 -0
  27. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/adapters/base_adapter.py +0 -0
  28. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  29. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/adapters/supabase_adapter.py +0 -0
  30. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/admin/FEATURE.md +0 -0
  31. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/admin/__init__.py +0 -0
  32. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/admin/router.py +0 -0
  33. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/api/__init__.py +0 -0
  34. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/api/auth.py +0 -0
  35. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/api/config.py +0 -0
  36. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/api/handlers.py +0 -0
  37. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/api/protocol.py +0 -0
  38. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/api/server.py +0 -0
  39. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/associations.py +0 -0
  40. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/cache_debug.py +0 -0
  41. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/catalog.py +0 -0
  42. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/catalog_sql.py +0 -0
  43. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/client/MODULE_README.md +0 -0
  44. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/client/__init__.py +0 -0
  45. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/client/postgres_connection.py +0 -0
  46. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/client/postgrest.py +0 -0
  47. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/client/supabase_auth.py +0 -0
  48. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/client/supabase_config.py +0 -0
  49. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/client/supabase_manager.py +0 -0
  50. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  51. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/MODULE_README.md +0 -0
  52. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  53. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/TASKS.md +0 -0
  54. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/__init__.py +0 -0
  55. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/admin_lane.py +0 -0
  56. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/base.py +0 -0
  57. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/connections.py +0 -0
  58. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/db_function.py +0 -0
  59. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/diagnostics.py +0 -0
  60. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/expressions.py +0 -0
  61. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/extended.py +0 -0
  62. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/fields.py +0 -0
  63. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/introspection.py +0 -0
  64. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/listen.py +0 -0
  65. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/loop_filters.py +0 -0
  66. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/model_dto.py +0 -0
  67. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/model_view.py +0 -0
  68. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/one_database.py +0 -0
  69. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/paginator.py +0 -0
  70. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/pool_maintenance.py +0 -0
  71. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/pool_watch.py +0 -0
  72. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/pooler_role.py +0 -0
  73. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/pydantic_bridge.py +0 -0
  74. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/registry.py +0 -0
  75. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/relations.py +0 -0
  76. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/resilience.py +0 -0
  77. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/rls_session.py +0 -0
  78. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/session_context.py +0 -0
  79. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/signals.py +0 -0
  80. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/tls.py +0 -0
  81. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/transaction.py +0 -0
  82. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/types.py +0 -0
  83. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/write_queue.py +0 -0
  84. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/core/write_retry.py +0 -0
  85. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/entity.py +0 -0
  86. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/error_handling.py +0 -0
  87. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/exceptions.py +0 -0
  88. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/extended/__init__.py +0 -0
  89. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/extended/app_error_handler.py +0 -0
  90. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/identity_rule.py +0 -0
  91. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/local_sqlite.py +0 -0
  92. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/middleware/__init__.py +0 -0
  93. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/middleware/base.py +0 -0
  94. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/MODULE_README.md +0 -0
  95. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/__init__.py +0 -0
  96. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/cli.py +0 -0
  97. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/ddl.py +0 -0
  98. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/diff.py +0 -0
  99. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/executor.py +0 -0
  100. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/integration.py +0 -0
  101. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/loader.py +0 -0
  102. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/operations.py +0 -0
  103. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/state.py +0 -0
  104. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/migrations/table_filter.py +0 -0
  105. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/no_org_assignment.py +0 -0
  106. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/FEATURE.md +0 -0
  107. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/MODULE_README.md +0 -0
  108. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/__init__.py +0 -0
  109. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/bulk_update_values.py +0 -0
  110. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/catalog_select.py +0 -0
  111. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/conflict.py +0 -0
  112. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/conflict_writes.py +0 -0
  113. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/create.py +0 -0
  114. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/db_functions.py +0 -0
  115. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/delete.py +0 -0
  116. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/dynamic_admin.py +0 -0
  117. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/dynamic_crud.py +0 -0
  118. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/graph_walk.py +0 -0
  119. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/latest_rows.py +0 -0
  120. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/queue_claim.py +0 -0
  121. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/read.py +0 -0
  122. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/relational_backfill.py +0 -0
  123. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/staging_load.py +0 -0
  124. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/operations/update.py +0 -0
  125. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/platform_access.py +0 -0
  126. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/platform_db.py +0 -0
  127. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/py.typed +0 -0
  128. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/pytest_live_db_guard.py +0 -0
  129. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/pytest_lock_guard.py +0 -0
  130. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/pytest_plugin.py +0 -0
  131. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/python_sql/MODULE_README.md +0 -0
  132. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/python_sql/__init__.py +0 -0
  133. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/python_sql/db_objects.py +0 -0
  134. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  135. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  136. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/query/FEATURE.md +0 -0
  137. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/query/MODULE_README.md +0 -0
  138. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/query/__init__.py +0 -0
  139. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/query/builder.py +0 -0
  140. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/query/executor.py +0 -0
  141. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/resources/__init__.py +0 -0
  142. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
  143. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
  144. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/FEATURE.md +0 -0
  145. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  146. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/__init__.py +0 -0
  147. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/code_handler.py +0 -0
  148. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  149. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/columns.py +0 -0
  150. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/common.py +0 -0
  151. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/diff_preview.py +0 -0
  152. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  153. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/generator.py +0 -0
  154. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  155. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  156. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  157. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  158. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/package_wiring.py +0 -0
  159. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/relationships.py +0 -0
  160. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/runner.py +0 -0
  161. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/schema.py +0 -0
  162. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/schema_manager.py +0 -0
  163. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/tables.py +0 -0
  164. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/schema_builder/views.py +0 -0
  165. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/__init__.py +0 -0
  166. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/attachments.py +0 -0
  167. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/backup_restore.py +0 -0
  168. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/crypto.py +0 -0
  169. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  170. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/model.py +0 -0
  171. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
  172. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/native_enable.py +0 -0
  173. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/native_lifecycle.py +0 -0
  174. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/native_passkeys.py +0 -0
  175. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/native_source.py +0 -0
  176. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/org_service.py +0 -0
  177. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/secrets_battery/service.py +0 -0
  178. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/README.md +0 -0
  179. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/__init__.py +0 -0
  180. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/coalesce.py +0 -0
  181. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/dag.py +0 -0
  182. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/errors.py +0 -0
  183. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/fallback.py +0 -0
  184. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/flush.py +0 -0
  185. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/lifecycle.py +0 -0
  186. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/managed.py +0 -0
  187. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/op.py +0 -0
  188. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/reads.py +0 -0
  189. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/session.py +0 -0
  190. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/session/telemetry.py +0 -0
  191. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  192. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/sql_executor/__init__.py +0 -0
  193. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/sql_executor/executor.py +0 -0
  194. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/sql_executor/queries.py +0 -0
  195. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/sql_executor/registry.py +0 -0
  196. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/sql_executor/types.py +0 -0
  197. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/sql_executor/utils.py +0 -0
  198. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/state.py +0 -0
  199. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/utils/__init__.py +0 -0
  200. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/utils/sql_utils.py +0 -0
  201. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/utils/type_converters.py +0 -0
  202. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/matrx_orm/write_actor.py +0 -0
  203. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/scripts/git-branches.sh +0 -0
  204. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/scripts/publish.sh +0 -0
  205. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/scripts/release.sh +0 -0
  206. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/MODULE_README.md +0 -0
  207. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/__init__.py +0 -0
  208. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/conftest.py +0 -0
  209. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/MODULE_README.md +0 -0
  210. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/__init__.py +0 -0
  211. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_adapter_registry.py +0 -0
  212. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_admin_db_columns.py +0 -0
  213. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_admin_response_rendering.py +0 -0
  214. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_admin_search_negation.py +0 -0
  215. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_admin_search_sentinels.py +0 -0
  216. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_advisory_lock.py +0 -0
  217. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_agent_message.py +0 -0
  218. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_api_auth.py +0 -0
  219. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_api_config.py +0 -0
  220. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_api_handlers.py +0 -0
  221. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_api_protocol.py +0 -0
  222. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_array_agg_order_by.py +0 -0
  223. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_association_trigger_actor_naming.py +0 -0
  224. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_associations.py +0 -0
  225. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_associations_wiring.py +0 -0
  226. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_bulk_hydration_offload.py +0 -0
  227. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_bulk_update_by_pk.py +0 -0
  228. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  229. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_cache_debug.py +0 -0
  230. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_call_function.py +0 -0
  231. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_case_when.py +0 -0
  232. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_catalog.py +0 -0
  233. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_committed_row_guard.py +0 -0
  234. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_composite_pk_filter.py +0 -0
  235. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_composite_pk_write_paths.py +0 -0
  236. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_config.py +0 -0
  237. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_conflict_target.py +0 -0
  238. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_conflict_writes.py +0 -0
  239. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_connection_codecs.py +0 -0
  240. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_connection_poison_guard.py +0 -0
  241. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_connection_primitives.py +0 -0
  242. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_count_composite_distinct.py +0 -0
  243. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_count_limit_param_leak.py +0 -0
  244. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_db_function_field.py +0 -0
  245. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_ddl_generator.py +0 -0
  246. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_deadlock_retry.py +0 -0
  247. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_derived_table.py +0 -0
  248. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_dirty_tracking.py +0 -0
  249. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_dynamic_crud.py +0 -0
  250. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_exceptions.py +0 -0
  251. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  252. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_expression_primitives.py +0 -0
  253. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_fields.py +0 -0
  254. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  255. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_fk_cycle_resolution.py +0 -0
  256. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_from_alias.py +0 -0
  257. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_fts.py +0 -0
  258. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_func_expression_args.py +0 -0
  259. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_graph_walk.py +0 -0
  260. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_having_annotate_alias.py +0 -0
  261. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  262. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_introspect_rls_policies.py +0 -0
  263. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_join_and_aggregate.py +0 -0
  264. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_latest_rows.py +0 -0
  265. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_listen_notify.py +0 -0
  266. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_local_sqlite.py +0 -0
  267. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_lock_contention_retry.py +0 -0
  268. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_managed_write_guard.py +0 -0
  269. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_matrx_entity.py +0 -0
  270. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_migration_diff_types.py +0 -0
  271. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_migration_loader.py +0 -0
  272. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_model_instance.py +0 -0
  273. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_model_meta.py +0 -0
  274. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_optimistic_concurrency.py +0 -0
  275. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_order_by_expression.py +0 -0
  276. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_org_assignment_inventory.py +0 -0
  277. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
  278. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_parameter_name_hydration.py +0 -0
  279. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_patch_jsonb_path.py +0 -0
  280. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_percentile_cont.py +0 -0
  281. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_persisted_count.py +0 -0
  282. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_pool_holder_census.py +0 -0
  283. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_pool_loop_recreation.py +0 -0
  284. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_pool_maintenance.py +0 -0
  285. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_postgrest_filters.py +0 -0
  286. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_pydantic_bridge.py +0 -0
  287. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_query_builder.py +0 -0
  288. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_query_error_detail.py +0 -0
  289. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_query_executor_sql.py +0 -0
  290. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_query_timeout.py +0 -0
  291. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  292. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_queue_claim.py +0 -0
  293. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_read_connection_retry.py +0 -0
  294. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_registry.py +0 -0
  295. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_registry_multi_database.py +0 -0
  296. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_registry_multi_schema.py +0 -0
  297. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_relational_backfill.py +0 -0
  298. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_relations.py +0 -0
  299. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_resilience.py +0 -0
  300. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_rls_session_scope.py +0 -0
  301. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_schema_exists.py +0 -0
  302. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_session_advisory_lock.py +0 -0
  303. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_standalone_write_session_context.py +0 -0
  304. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_state_cache.py +0 -0
  305. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_subquery_filter_raw.py +0 -0
  306. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_subquery_in_filter.py +0 -0
  307. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_subquery_nested_expressions.py +0 -0
  308. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_supabase_auth.py +0 -0
  309. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_supabase_config.py +0 -0
  310. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_tls.py +0 -0
  311. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_transaction_database_identity.py +0 -0
  312. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_typed_join_projection.py +0 -0
  313. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_typed_predicates.py +0 -0
  314. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_update_case_expression.py +0 -0
  315. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_update_subquery.py +0 -0
  316. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_update_with_rebase.py +0 -0
  317. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_upsert_default_leak.py +0 -0
  318. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_upsert_with_conflict.py +0 -0
  319. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_values_jsonb_decode.py +0 -0
  320. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_watched_lifecycle.py +0 -0
  321. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_write_queue_deadline.py +0 -0
  322. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level1/test_write_retry_semantics.py +0 -0
  323. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/MODULE_README.md +0 -0
  324. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/__init__.py +0 -0
  325. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/conftest.py +0 -0
  326. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_bulk_ops.py +0 -0
  327. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_cache_integration.py +0 -0
  328. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_crud.py +0 -0
  329. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_foreign_keys.py +0 -0
  330. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_m2m.py +0 -0
  331. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_manager.py +0 -0
  332. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_migrations_live.py +0 -0
  333. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_query_execution.py +0 -0
  334. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/test_schema_diff.py +0 -0
  335. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
  336. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/live_pooler_role.py +0 -0
  337. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/native_source_cases.py +0 -0
  338. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/native_source_oracle.rs +0 -0
  339. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project/.env.example +0 -0
  340. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project/README.md +0 -0
  341. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project/__init__.py +0 -0
  342. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project/generate.py +0 -0
  343. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project/generated/.gitkeep +0 -0
  344. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project/matrx_orm.yaml +0 -0
  345. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project/test_schema_generation.py +0 -0
  346. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project_desktop/.env.example +0 -0
  347. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project_desktop/README.md +0 -0
  348. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project_desktop/__init__.py +0 -0
  349. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project_desktop/client_example.py +0 -0
  350. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project_desktop/client_example.ts +0 -0
  351. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  352. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/sample_project_desktop/server.py +0 -0
  353. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/entity_tests.py +0 -0
  354. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_base_generation.py +0 -0
  355. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  356. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_composite_unique_generation.py +0 -0
  357. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_dto_identity_generation.py +0 -0
  358. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_entity_capability_generation.py +0 -0
  359. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_enum_ordering.py +0 -0
  360. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_generate_schema.py +0 -0
  361. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_junction_analysis.py +0 -0
  362. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_multi_schema_output.py +0 -0
  363. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_schema_analysis.py +0 -0
  364. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_sql_expression_defaults.py +0 -0
  365. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_table_relationships_query.py +0 -0
  366. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema/test_view_generation.py +0 -0
  367. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema_builder/test_array_default_parsing.py +0 -0
  368. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  369. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  370. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/__init__.py +0 -0
  371. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/conftest.py +0 -0
  372. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_a_failed_capture_still_names_the_failure.py +0 -0
  373. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_cache_no_downgrade.py +0 -0
  374. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_capture_poison_proof.py +0 -0
  375. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_coalesce.py +0 -0
  376. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_dag.py +0 -0
  377. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_disk_spill_fallback.py +0 -0
  378. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_flush_individual_fallback.py +0 -0
  379. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_governed_write.py +0 -0
  380. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_live_db_guard_plugin.py +0 -0
  381. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_managed.py +0 -0
  382. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_op_enqueue_site.py +0 -0
  383. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_reads.py +0 -0
  384. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_record_error_bounded_insert.py +0 -0
  385. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_record_error_storm.py +0 -0
  386. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_session.py +0 -0
  387. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/session/test_spill_guard_plugin.py +0 -0
  388. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_array_arg_element_type.py +0 -0
  389. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_attachment_mutation_serialization.py +0 -0
  390. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_backup_alias_conflict.py +0 -0
  391. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_backup_restore_live.py +0 -0
  392. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_composite_fk_is_not_a_column_fk.py +0 -0
  393. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_database_name_alias.py +0 -0
  394. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_db_functions.py +0 -0
  395. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_dsn_password_masking.py +0 -0
  396. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_dynamic_select_unregistered.py +0 -0
  397. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_immutable_insert_or_exact_verify.py +0 -0
  398. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_keyed_digest.py +0 -0
  399. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_model_cls_refactor.py +0 -0
  400. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_native_enable.py +0 -0
  401. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_native_lifecycle.py +0 -0
  402. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_native_lifecycle_fences.py +0 -0
  403. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_native_passkey_receipts.py +0 -0
  404. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_native_source.py +0 -0
  405. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  406. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_one_database.py +0 -0
  407. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_order_by_desc_nulls_last.py +0 -0
  408. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_pooler_role_guard.py +0 -0
  409. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_secret_audit_names_its_organization.py +0 -0
  410. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_secrets_minting_keys.py +0 -0
  411. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_shared_lock_guard.py +0 -0
  412. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_sql_param_casts.py +0 -0
  413. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_sync_twins_guard.py +0 -0
  414. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_user_visible_lookup.py +0 -0
  415. {matrx_orm-3.1.182 → matrx_orm-3.1.183}/tests/test_vault_aggregate_recovery_stateful.py +0 -0
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ - Pool creation always ends. A pool `init` failure now aborts the connection
6
+ (asyncpg's own failure path awaited an untimed graceful close), and
7
+ `asyncpg.create_pool` runs under one deadline (`pool_create_timeout`, default
8
+ 30 s; `connect_timeout`, default 10 s, both on `DatabaseProjectConfig`), with
9
+ the half-built pool terminated on failure. Before this, a database that hung
10
+ mid-handshake held the per-config pool lock forever: files.matrxserver.com
11
+ answered nothing that touched the database for 4.5 h on 2026-09-25.
12
+ Guard: `tests/level1/test_pool_create_never_wedges.py`.
13
+
14
+ ## 3.1.182
15
+
16
+ - 3.1.181 was tagged against a commit that did not carry the version and never
17
+ 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.182
3
+ Version: 3.1.183
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
@@ -169,6 +169,20 @@ connection and can strand old holders in release/reset.
169
169
 
170
170
  Pinned by `tests/level1/test_pool_maintenance.py`.
171
171
 
172
+ **Pool creation always ends.** `get_pool` creates under the config's lock, so an
173
+ unbounded creation wedges every caller of that config. Two layers: (1) the
174
+ `init` callback (`_init_connection_default`) ABORTS its connection on any
175
+ failure or cancellation — asyncpg's own failure path awaits `con.close()` with
176
+ no timeout, after the one cancellation an outer timeout could deliver was
177
+ already spent, and against a hung database that close never ends; (2)
178
+ `asyncpg.create_pool` runs under `pool_create_timeout` (default 30 s) with
179
+ `connect_timeout` (default 10 s), and the half-built pool is terminated on
180
+ failure. Incident 2026-09-25: the zero-connection replacement ran while the
181
+ database machine was hung and held `_locks['matrx_files']` for 4.5 h (the same
182
+ close wedged matrx-seo's keepalive). Pinned by
183
+ `tests/level1/test_pool_create_never_wedges.py` (a fake PostgreSQL that
184
+ handshakes, then goes silent).
185
+
172
186
  **A server restart rejected during checkout is a connection failure.** PostgreSQL
173
187
  `AdminShutdownError`, `CrashShutdownError`, and `CannotConnectNowError` raised
174
188
  while `pool.acquire()` opens a physical connection become `ConnectionError` at
@@ -370,5 +384,6 @@ Verified against the live database: 2026-08-21.
370
384
  - 2026-08-14: Public models now render `public.table` SQL targets; bare public
371
385
  relation names can no longer disable application logs or write-failure capture.
372
386
  - 2026-08-13: Registry, admin, recovery, catalog, and diagnostic identities are now exact `schema.table`; bare names are accepted only as uncached discovery terms and ambiguity fails loudly.
387
+ - 2026-09-25: Pool creation is bounded and a failed connection init aborts instead of awaiting an untimed close.
373
388
  - 2026-08-13: Added atomic recovery from a pool left at zero connections after an upstream database restart.
374
389
  - 2026-08-09: Pinned positional-mapping hydration for parameter-shaped column names.
@@ -119,6 +119,11 @@ def _integrity_constraint_kind(exc: asyncpg.exceptions.IntegrityConstraintViolat
119
119
  POOL_ACQUIRE_RETRIES = 2 # total acquire attempts = 1 + POOL_ACQUIRE_RETRIES
120
120
  POOL_ACQUIRE_RETRY_BASE_BACKOFF = 0.25 # seconds; doubled per attempt
121
121
  POOL_RELEASE_TIMEOUT_SECONDS = 10.0
122
+ # Pool-creation bounds (per-config overrides: ``connect_timeout`` /
123
+ # ``pool_create_timeout`` on DatabaseProjectConfig). Creation runs under the
124
+ # config's pool lock, so it must always end — see _init_connection_default.
125
+ DEFAULT_CONNECT_TIMEOUT_SECONDS = 10.0
126
+ DEFAULT_POOL_CREATE_TIMEOUT_SECONDS = 30.0
122
127
 
123
128
  # ── read-statement retry (command-timeout self-heal) ──────────────────────────
124
129
  # A ``command_timeout`` on a READ (SELECT) is provably side-effect-free to retry
@@ -257,6 +262,30 @@ def _encode_json(value: Any) -> str:
257
262
 
258
263
 
259
264
  async def _init_connection_default(conn: asyncpg.Connection) -> None:
265
+ """Register the codecs on a new connection; a failure ABORTS it.
266
+
267
+ 🚨 THE 2026-09-25 FILES OUTAGE. When a pool ``init`` callback raises,
268
+ asyncpg's ``Pool._get_new_connection`` runs ``await con.close()`` with NO
269
+ timeout before re-raising. Against a database that has stopped answering
270
+ (a hung machine: handshake done, then silence, no FIN, no RST) that graceful
271
+ close never finishes — and when it happens inside ``asyncpg.create_pool``,
272
+ matrx-orm's per-config pool-creation lock is held forever and every later
273
+ request queues behind it without a log line. files.matrxserver.com sat
274
+ exactly there for 4.5 hours. Aborting the transport here makes the
275
+ connection already closed, so asyncpg's ``close()`` returns at once and the
276
+ original error reaches the caller.
277
+ """
278
+ try:
279
+ await _register_connection_codecs(conn)
280
+ except BaseException:
281
+ try:
282
+ conn.terminate()
283
+ except Exception: # noqa: BLE001 — abort is best-effort; the original error wins
284
+ pass
285
+ raise
286
+
287
+
288
+ async def _register_connection_codecs(conn: asyncpg.Connection) -> None:
260
289
  """Register custom type codecs on each new connection.
261
290
 
262
291
  UUID codec — makes asyncpg decode PostgreSQL uuid columns directly as Python
@@ -610,7 +639,21 @@ class AsyncDatabaseManager:
610
639
  # Bootstrap exactly one connection; the bounded
611
640
  # maintenance controller ramps toward the desired floor.
612
641
  bootstrap_min = min(desired_pool_min, 1)
613
- pool = await asyncpg.create_pool(
642
+ connect_timeout = float(
643
+ config.get("connect_timeout") or DEFAULT_CONNECT_TIMEOUT_SECONDS
644
+ )
645
+ create_timeout = float(
646
+ config.get("pool_create_timeout")
647
+ or DEFAULT_POOL_CREATE_TIMEOUT_SECONDS
648
+ )
649
+ # 🚨 Pool creation runs under this config's lock, so it
650
+ # must END. asyncpg bounds the connect itself
651
+ # (``timeout=``) but not the ``init`` callback's failure
652
+ # path nor the pool as a whole; a hung database once held
653
+ # this lock for 4.5 hours (see _init_connection_default).
654
+ # Build the pool object, await it under one deadline,
655
+ # and terminate whatever it half-built on any failure.
656
+ new_pool = asyncpg.create_pool(
614
657
  host=config["host"],
615
658
  port=config["port"],
616
659
  database=config["database_name"],
@@ -624,7 +667,29 @@ class AsyncDatabaseManager:
624
667
  statement_cache_size=statement_cache_size,
625
668
  server_settings=server_settings,
626
669
  init=_init_connection_default,
670
+ timeout=connect_timeout,
627
671
  )
672
+ try:
673
+ async with asyncio.timeout(create_timeout):
674
+ pool = await new_pool
675
+ except BaseException as create_exc:
676
+ try:
677
+ new_pool.terminate()
678
+ except Exception: # noqa: BLE001 — a pool that never initialised has nothing to abort
679
+ pass
680
+ if isinstance(create_exc, TimeoutError):
681
+ logger.error(
682
+ "[matrx_orm] Pool creation for '%s' did not finish within "
683
+ "%.1fs (connect timeout %.1fs) — the database accepted "
684
+ "the connection but stopped answering. The half-built "
685
+ "pool was terminated; the next request tries again.",
686
+ config_name,
687
+ create_timeout,
688
+ connect_timeout,
689
+ )
690
+ # A TimeoutError is classified below as a transport
691
+ # timeout (DatabaseConnectError) — one honest banner.
692
+ raise
628
693
  cls._pools[config_name] = pool
629
694
 
630
695
  logger.info(
@@ -53,6 +53,13 @@ class DatabaseProjectConfig:
53
53
  pool_min: int = 5
54
54
  pool_max: int = 30
55
55
  command_timeout: int = 10
56
+ # Bounds on opening a pool. ``connect_timeout`` is asyncpg's per-connection
57
+ # connect deadline; ``pool_create_timeout`` bounds the whole creation
58
+ # (connect + the init callback + asyncpg's own cleanup). Creation holds the
59
+ # config's pool lock, so an unbounded creation wedges every caller: the
60
+ # 2026-09-25 files.matrxserver.com outage (4.5 h behind one stuck create).
61
+ connect_timeout: float = 10.0
62
+ pool_create_timeout: float = 30.0
56
63
 
57
64
  # Maximum seconds an idle connection may sit in the pool before it is
58
65
  # closed and replaced. asyncpg's own default is 300s, which is *longer*
@@ -240,6 +247,8 @@ class DatabaseRegistry:
240
247
  "pool_min": config.pool_min,
241
248
  "pool_max": config.pool_max,
242
249
  "command_timeout": config.command_timeout,
250
+ "connect_timeout": config.connect_timeout,
251
+ "pool_create_timeout": config.pool_create_timeout,
243
252
  "max_inactive_connection_lifetime": config.max_inactive_connection_lifetime,
244
253
  "server_settings": dict(config.server_settings),
245
254
  "ssl": config.ssl,
@@ -43,8 +43,12 @@ keys, typed identifiers and timestamp, locked original ownership, and empty
43
43
  component arrays. Organization-owned history refuses until the host supplies the
44
44
  grant/offboarding parent-lock contract. Future aggregate capture may use the
45
45
  revision-1/revision-2 bootstrap only after its producer, authority, retention,
46
- rotation, and reader gates are independently accepted. Gate B must freeze and
47
- validate the encrypted component shape before the writer can accept a snapshot.
46
+ rotation, and reader gates are independently accepted. Gate B's source-only
47
+ 1047 validator freezes one personal, active ordinary password field shape: its
48
+ first recorded update is an A=A bootstrap followed by A→B, and later records
49
+ are one prior-after-matched delta each. Application capture is explicitly off
50
+ until every producer and reader gate is accepted; 1047 is never applied outside
51
+ the named runner's reviewed rehearsal.
48
52
 
49
53
  Items may also own N encrypted **file attachments**
50
54
  (`users.credential_attachments`). An attachment has its own stable id,
@@ -156,6 +160,8 @@ drops every grantee when `access_mode='all_members'` — it survives for the org
156
160
  access-mode flip, but building new callers on it is how a share edit silently
157
161
  revokes someone. `list_granted_credential_items(user_id)` is the
158
162
  "shared with me" read.
163
+ `set_item_grants` locks the live parent before deleting or replacing grants;
164
+ host readers and revokers therefore serialize parent-first.
159
165
 
160
166
  `transfer_credential_item` clears prior grants by default (`clear_grants=True`)
161
167
  and writes BOTH `item_transferred` and `item_transferred_out`, so the losing
@@ -381,6 +387,10 @@ delete).
381
387
 
382
388
  ## Change Log
383
389
 
390
+ - **2026-09-25** — `set_item_grants` now locks the live parent before grant
391
+ replacement, matching the host bulk reader's revocation order. A focused
392
+ ordering test fails if deletion moves ahead of the parent lock.
393
+
384
394
  - **2026-09-24** — Migration 1036 closes the personal-only Vault revision envelope and refuses organization history pending the canonical grant/offboarding parent lock; capture remains disabled.
385
395
 
386
396
  - **2026-09-24** — Kept the history counter out of the ORM until migrations are installed; the branch database lacks the column and a premature model mapping broke ordinary Vault creation. Capture remains disabled.
@@ -1327,7 +1327,7 @@ async def update_credential_field_value(
1327
1327
  _PASSWORD_HISTORY_CAPTURE_ENABLED and not system_rotation
1328
1328
  and item_raw is not None and item_raw.get("user_id") is not None
1329
1329
  and raw.get("field_key") == "password" and raw.get("handling") in {"visible", "revealable"}
1330
- and raw.get("execution_purpose") == GENERAL_EXECUTION_PURPOSE
1330
+ and raw.get("execution_purpose") == GENERAL_EXECUTION_PURPOSE and raw.get("is_active") is True
1331
1331
  )
1332
1332
  history_revision: int | None = None
1333
1333
  capture_started_at = datetime.now(UTC)
@@ -1335,17 +1335,18 @@ async def update_credential_field_value(
1335
1335
  if not actor_id or not audit_organization_id:
1336
1336
  raise VaultHistorySnapshotError("personal password history requires actor and admitted audit organization")
1337
1337
  history_revision = await locked_history_revision(str(item_raw["id"]))
1338
- before = password_field_update_snapshot(
1339
- item_id=str(item_raw["id"]), item_user_id=str(item_raw["user_id"]),
1340
- audit_organization_id=audit_organization_id, field_id=field_id,
1341
- field_key=str(raw["field_key"]), handling=str(raw["handling"]),
1342
- execution_purpose=str(raw["execution_purpose"]), before_encrypted=raw["value_encrypted"],
1343
- after_encrypted=raw["value_encrypted"], capture_started_at=capture_started_at,
1344
- )
1345
- history_revision = await write_password_field_update_snapshot(
1346
- item_id=str(item_raw["id"]), actor_id=actor_id, audit_organization_id=audit_organization_id,
1347
- expected_revision=history_revision, snapshot=before,
1348
- )
1338
+ if history_revision == 0:
1339
+ before = password_field_update_snapshot(
1340
+ item_id=str(item_raw["id"]), item_user_id=str(item_raw["user_id"]),
1341
+ audit_organization_id=audit_organization_id, field_id=field_id,
1342
+ field_key=str(raw["field_key"]), handling=str(raw["handling"]),
1343
+ execution_purpose=str(raw["execution_purpose"]), before_encrypted=raw["value_encrypted"],
1344
+ after_encrypted=raw["value_encrypted"], capture_started_at=capture_started_at,
1345
+ )
1346
+ history_revision = await write_password_field_update_snapshot(
1347
+ item_id=str(item_raw["id"]), actor_id=actor_id, audit_organization_id=audit_organization_id,
1348
+ expected_revision=history_revision, snapshot=before,
1349
+ )
1349
1350
  encrypted_value = encrypt_value(value)
1350
1351
  await UserSecret.update_where(
1351
1352
  {"id": field_id, "deleted_at__isnull": True},
@@ -1800,7 +1801,6 @@ async def set_item_grants(
1800
1801
  recipient is a legitimate member first."""
1801
1802
  if access_mode not in ("all_members", "restricted"):
1802
1803
  raise ValueError(f"access_mode must be all_members|restricted; got {access_mode!r}")
1803
- item_raw = await _require_item_raw(item_id)
1804
1804
  # Dedup by user_id, last spec wins.
1805
1805
  deduped: dict[str, ItemGrantSpec] = {}
1806
1806
  if access_mode == "restricted":
@@ -1810,6 +1810,10 @@ async def set_item_grants(
1810
1810
  deduped[g.user_id] = g
1811
1811
  recipients = [deduped[uid] for uid in sorted(deduped)]
1812
1812
  async with transaction(PACKAGE_DB_NAME):
1813
+ # Readers and revokers share parent-first serialization. Deleting grant
1814
+ # rows before this lock inverts the order and can deadlock with a
1815
+ # plaintext reader holding the parent while acquiring its grant.
1816
+ item_raw = await _lock_item_raw(item_id)
1813
1817
  await UserSecretGrant.delete_where(credential_item_id=item_id)
1814
1818
  for g in recipients:
1815
1819
  await UserSecretGrant.create(
@@ -121,12 +121,25 @@ def validate_snapshot_v1(snapshot: dict[str, Any]) -> None:
121
121
  raise VaultHistorySnapshotError("Vault history state is not valid encrypted v1 data")
122
122
 
123
123
 
124
+ def validate_snapshot_revision(snapshot: dict[str, Any], expected_revision: int) -> None:
125
+ """Bind the bootstrap shape to revision zero and every later shape to a delta."""
126
+ validate_snapshot_v1(snapshot)
127
+ if expected_revision < 0:
128
+ raise VaultHistorySnapshotError("Vault history revision must be non-negative")
129
+ change = snapshot["changed_components"][0]
130
+ same_state = change["before"] == change["after"]
131
+ if expected_revision == 0 and not same_state:
132
+ raise VaultHistorySnapshotError("Vault history revision one must be the A=A bootstrap")
133
+ if expected_revision > 0 and same_state:
134
+ raise VaultHistorySnapshotError("Vault history revisions after bootstrap must be deltas")
135
+
136
+
124
137
  async def write_password_field_update_snapshot(
125
138
  *, item_id: str, actor_id: str, audit_organization_id: str, expected_revision: int,
126
139
  snapshot: dict[str, Any],
127
140
  ) -> int:
128
141
  """Call the 1034 private writer inside the caller's active transaction."""
129
- validate_snapshot_v1(snapshot)
142
+ validate_snapshot_revision(snapshot, expected_revision)
130
143
  rows = await call_function(
131
144
  PACKAGE_DB_NAME, "history", "vault_write_revision", item_id, actor_id,
132
145
  audit_organization_id, expected_revision, snapshot, mode="rows",
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.1.182"
3
+ version = "3.1.183"
4
4
  description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -0,0 +1,194 @@
1
+ """A pool created against a database that stops answering must fail in bounded time.
2
+
3
+ Incident 2026-09-25 (files.matrxserver.com): the production database machine hung
4
+ 14:30–14:38 UTC. matrx-orm's zero-connection self-heal replaced the dead pool and
5
+ called ``asyncpg.create_pool`` while the database was still hung. The new
6
+ connection completed its handshake, then the pool ``init`` callback
7
+ (``_init_connection_default`` -> ``set_type_codec`` introspection) timed out, and
8
+ asyncpg's failure path ran ``await con.close()`` with NO timeout. The graceful
9
+ close never finished, ``create_pool`` never returned, and the per-config
10
+ pool-creation lock stayed held for 4.5 hours: every request queued behind it with
11
+ no log and no socket, until the container was restarted.
12
+
13
+ This test reproduces that exact wire situation with a fake PostgreSQL server
14
+ that completes the startup handshake and then goes silent forever (it never
15
+ answers a query and never closes the socket — what a hung machine looks like to
16
+ a client). It drives the REAL ``AsyncDatabaseManager.get_connection`` ->
17
+ ``get_pool`` -> ``asyncpg.create_pool`` path with the real ``init`` callback.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import asyncio
23
+ import struct
24
+ import time
25
+
26
+ import pytest
27
+ from matrx_orm.core.async_db_manager import AsyncDatabaseManager
28
+ from matrx_orm.core.config import DatabaseProjectConfig, register_database, registry
29
+
30
+
31
+ def _msg(kind: bytes, payload: bytes) -> bytes:
32
+ return kind + struct.pack("!I", len(payload) + 4) + payload
33
+
34
+
35
+ def _param(name: str, value: str) -> bytes:
36
+ return _msg(b"S", name.encode() + b"\0" + value.encode() + b"\0")
37
+
38
+
39
+ class HungAfterHandshakeServer:
40
+ """Speaks the PostgreSQL startup protocol, then never answers again."""
41
+
42
+ def __init__(self) -> None:
43
+ self.connections = 0
44
+ self.writers: list[asyncio.StreamWriter] = []
45
+ self.server: asyncio.base_events.Server | None = None
46
+ self.port = 0
47
+
48
+ async def start(self) -> None:
49
+ self.server = await asyncio.start_server(self._handle, "127.0.0.1", 0)
50
+ self.port = self.server.sockets[0].getsockname()[1]
51
+
52
+ async def _handle(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
53
+ self.connections += 1
54
+ self.writers.append(writer)
55
+ try:
56
+ (length,) = struct.unpack("!I", await reader.readexactly(4))
57
+ await reader.readexactly(length - 4) # StartupMessage (ssl disabled)
58
+ writer.write(
59
+ _msg(b"R", struct.pack("!I", 0)) # AuthenticationOk
60
+ + _param("server_version", "17.0")
61
+ + _param("server_encoding", "UTF8")
62
+ + _param("client_encoding", "UTF8")
63
+ + _param("DateStyle", "ISO, MDY")
64
+ + _param("integer_datetimes", "on")
65
+ + _param("standard_conforming_strings", "on")
66
+ + _msg(b"K", struct.pack("!II", 1234, 5678)) # BackendKeyData
67
+ + _msg(b"Z", b"I") # ReadyForQuery
68
+ )
69
+ await writer.drain()
70
+ # Hung machine: read and drop everything (queries, Sync, Terminate);
71
+ # never reply, never close.
72
+ while await reader.read(65536):
73
+ pass
74
+ except (asyncio.IncompleteReadError, ConnectionError):
75
+ pass
76
+ # Peer closed: stay silent, keep our side open until the test ends.
77
+ await asyncio.Event().wait()
78
+
79
+ async def stop(self) -> None:
80
+ for w in self.writers:
81
+ w.transport.abort()
82
+ if self.server is not None:
83
+ self.server.close()
84
+
85
+
86
+ def _register(name: str, port: int, *, command_timeout: int = 1, create_timeout: float = 4.0) -> None:
87
+ registry._configs.pop(name, None)
88
+ register_database(
89
+ DatabaseProjectConfig(
90
+ name=name,
91
+ alias=f"{name}_alias",
92
+ host="127.0.0.1",
93
+ port=str(port),
94
+ database_name="postgres",
95
+ user="postgres",
96
+ password="unused",
97
+ pool_min=1,
98
+ pool_max=2,
99
+ command_timeout=command_timeout,
100
+ ssl=False,
101
+ write_queue_enabled=False,
102
+ connect_timeout=2.0,
103
+ pool_create_timeout=create_timeout,
104
+ )
105
+ )
106
+
107
+
108
+ def _forget(name: str) -> None:
109
+ pool = AsyncDatabaseManager._pools.pop(name, None)
110
+ if pool is not None:
111
+ try:
112
+ pool.terminate()
113
+ except Exception:
114
+ pass
115
+ AsyncDatabaseManager._pool_loops.pop(name, None)
116
+ AsyncDatabaseManager._locks.pop(name, None)
117
+ AsyncDatabaseManager._growth_locks.pop(name, None)
118
+ task = AsyncDatabaseManager._keepalive_tasks.pop(name, None)
119
+ if task is not None:
120
+ task.cancel()
121
+ registry._configs.pop(name, None)
122
+
123
+
124
+ @pytest.mark.asyncio
125
+ @pytest.mark.parametrize(
126
+ ("command_timeout", "create_timeout"),
127
+ [
128
+ # init's introspection times out first (files.matrxserver.com, 14:32 UTC)
129
+ (1, 4.0),
130
+ # the outer deadline CANCELS init first. asyncpg then awaits its cleanup
131
+ # close() after the one cancellation was already spent — the trap that
132
+ # also wedged matrx-seo's keepalive (maintain_one_connection's 5 s timeout
133
+ # cancelled init; the close that followed hung for 4.5 h).
134
+ (30, 2.0),
135
+ ],
136
+ ids=["init-times-out", "deadline-cancels-init"],
137
+ )
138
+ async def test_pool_creation_against_a_hung_database_fails_in_bounded_time_and_releases_the_lock(
139
+ command_timeout: int, create_timeout: float
140
+ ):
141
+ name = f"hung_after_handshake_{command_timeout}"
142
+ server = HungAfterHandshakeServer()
143
+ await server.start()
144
+ _register(name, server.port, command_timeout=command_timeout, create_timeout=create_timeout)
145
+ try:
146
+ # First caller: must FAIL (not hang) well inside the create deadline + slack.
147
+ started = time.monotonic()
148
+ with pytest.raises(Exception):
149
+ async with asyncio.timeout(15): # the test's own backstop, far above the knob
150
+ async with AsyncDatabaseManager.get_connection(name, timeout=1.0):
151
+ pass
152
+ first_elapsed = time.monotonic() - started
153
+ assert first_elapsed < 10, f"pool creation took {first_elapsed:.1f}s against a hung DB"
154
+
155
+ # The pool-creation lock must be free again: a wedged lock is the outage.
156
+ lock = AsyncDatabaseManager._locks.get(name)
157
+ assert lock is not None and not lock.locked(), "pool-creation lock left held"
158
+ assert name not in AsyncDatabaseManager._pools, "a half-initialised pool was kept"
159
+
160
+ # Second caller reaches the database again (a new connection attempt), and
161
+ # also fails in bounded time — nobody queues forever behind the first.
162
+ before = server.connections
163
+ started = time.monotonic()
164
+ with pytest.raises(Exception):
165
+ async with asyncio.timeout(15):
166
+ async with AsyncDatabaseManager.get_connection(name, timeout=1.0):
167
+ pass
168
+ assert time.monotonic() - started < 10
169
+ assert server.connections > before, "second caller never reached the database"
170
+ finally:
171
+ _forget(name)
172
+ await server.stop()
173
+
174
+
175
+ @pytest.mark.asyncio
176
+ @pytest.mark.parametrize("failure", [TimeoutError, asyncio.CancelledError])
177
+ async def test_failed_init_aborts_the_connection_instead_of_a_graceful_close_that_can_hang(failure):
178
+ """The init callback's own failure — or its cancellation — leaves asyncpg nothing to wait on."""
179
+ from matrx_orm.core.async_db_manager import _init_connection_default
180
+
181
+ class _Conn:
182
+ def __init__(self) -> None:
183
+ self.terminated = False
184
+
185
+ async def set_type_codec(self, *_a, **_k):
186
+ raise failure("introspection timed out on a hung database")
187
+
188
+ def terminate(self) -> None:
189
+ self.terminated = True
190
+
191
+ conn = _Conn()
192
+ with pytest.raises(failure):
193
+ await _init_connection_default(conn) # type: ignore[arg-type]
194
+ assert conn.terminated, "a failed init must abort the connection"
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import base64
13
13
  import hashlib
14
+ from contextlib import asynccontextmanager
14
15
  from datetime import UTC, datetime
15
16
 
16
17
  import pytest
@@ -880,6 +881,35 @@ async def test_set_item_grants_validates_access_mode_before_touching_db():
880
881
  await items.set_item_grants("i1", actor_id="u1", access_mode="nope", grants=[])
881
882
 
882
883
 
884
+ @pytest.mark.asyncio
885
+ async def test_set_item_grants_locks_parent_before_deleting_grants(monkeypatch):
886
+ from matrx_orm.secrets_battery import items
887
+
888
+ events: list[str] = []
889
+
890
+ @asynccontextmanager
891
+ async def fake_transaction(_database):
892
+ yield
893
+
894
+ async def lock_parent(_item_id):
895
+ events.append("parent")
896
+ return {"id": "i1"}
897
+
898
+ async def delete_grants(**_filters):
899
+ events.append("grant_delete")
900
+ raise RuntimeError("stop after order proof")
901
+
902
+ monkeypatch.setattr(items, "transaction", fake_transaction)
903
+ monkeypatch.setattr(items, "_lock_item_raw", lock_parent)
904
+ monkeypatch.setattr(items.UserSecretGrant, "delete_where", delete_grants)
905
+
906
+ with pytest.raises(RuntimeError, match="order proof"):
907
+ await items.set_item_grants(
908
+ "i1", actor_id="u1", access_mode="restricted", grants=[]
909
+ )
910
+ assert events == ["parent", "grant_delete"]
911
+
912
+
883
913
  # ── Rotation primitives (I-7) ─────────────────────────────────────────────
884
914
 
885
915
 
@@ -1145,7 +1175,10 @@ def test_password_history_snapshot_v1_excludes_plaintext_and_hints(monkeypatch):
1145
1175
  vault_history.validate_snapshot_v1(snapshot)
1146
1176
 
1147
1177
 
1148
- def _history_update_fakes(monkeypatch, *, writer_error: Exception | None = None):
1178
+ def _history_update_fakes(
1179
+ monkeypatch, *, writer_error: Exception | None = None, writer_error_call: int | None = None,
1180
+ revision: int = 0,
1181
+ ):
1149
1182
  from matrx_orm.secrets_battery import items
1150
1183
 
1151
1184
  old_ciphertext = _history_ciphertext()
@@ -1179,7 +1212,7 @@ def _history_update_fakes(monkeypatch, *, writer_error: Exception | None = None)
1179
1212
 
1180
1213
  async def _writer(**kwargs):
1181
1214
  snapshots.append(kwargs["snapshot"])
1182
- if writer_error:
1215
+ if writer_error and (writer_error_call is None or len(snapshots) == writer_error_call):
1183
1216
  raise writer_error
1184
1217
  return kwargs["expected_revision"] + 1
1185
1218
 
@@ -1189,7 +1222,7 @@ def _history_update_fakes(monkeypatch, *, writer_error: Exception | None = None)
1189
1222
  monkeypatch.setattr(items, "transaction", _FakeTxn())
1190
1223
  monkeypatch.setattr(items, "write_password_field_update_snapshot", _writer)
1191
1224
  async def _revision(_item_id):
1192
- return 0
1225
+ return revision
1193
1226
  monkeypatch.setattr(items, "locked_history_revision", _revision)
1194
1227
  monkeypatch.setattr(items, "record_secret_audit", _noop_audit)
1195
1228
  return items, updates, snapshots
@@ -1220,3 +1253,66 @@ async def test_password_update_does_not_mutate_when_history_write_fails(monkeypa
1220
1253
  audit_organization_id="00000000-0000-0000-0000-000000000003",
1221
1254
  )
1222
1255
  assert updates == []
1256
+
1257
+
1258
+ def test_password_history_revision_shape_allows_only_one_bootstrap(monkeypatch):
1259
+ from matrx_orm.secrets_battery import vault_history
1260
+
1261
+ monkeypatch.setenv("CREDENTIALS_ENCRYPTION_KEY", _fernet_key())
1262
+ ciphertext = _history_ciphertext()
1263
+ bootstrap = vault_history.password_field_update_snapshot(
1264
+ item_id="00000000-0000-0000-0000-000000000001", item_user_id="00000000-0000-0000-0000-000000000002",
1265
+ audit_organization_id="00000000-0000-0000-0000-000000000003", field_id="00000000-0000-0000-0000-000000000004",
1266
+ field_key="password", handling="revealable", execution_purpose="general", before_encrypted=ciphertext,
1267
+ after_encrypted=ciphertext, capture_started_at=datetime.now(UTC),
1268
+ )
1269
+ vault_history.validate_snapshot_revision(bootstrap, 0)
1270
+ with pytest.raises(vault_history.VaultHistorySnapshotError, match="must be deltas"):
1271
+ vault_history.validate_snapshot_revision(bootstrap, 2)
1272
+
1273
+
1274
+ @pytest.mark.asyncio
1275
+ async def test_password_update_after_bootstrap_writes_one_delta(monkeypatch):
1276
+ monkeypatch.setenv("CREDENTIALS_ENCRYPTION_KEY", _fernet_key())
1277
+ items, _updates, snapshots = _history_update_fakes(monkeypatch, revision=2)
1278
+ await items.update_credential_field_value(
1279
+ "00000000-0000-0000-0000-000000000004", "synthetic-password-c", actor_id="00000000-0000-0000-0000-000000000002",
1280
+ audit_organization_id="00000000-0000-0000-0000-000000000003",
1281
+ )
1282
+ assert len(snapshots) == 1
1283
+ assert snapshots[0]["changed_components"][0]["before"] != snapshots[0]["changed_components"][0]["after"]
1284
+
1285
+
1286
+ @pytest.mark.asyncio
1287
+ async def test_second_history_write_failure_propagates_from_atomic_transaction(monkeypatch):
1288
+ monkeypatch.setenv("CREDENTIALS_ENCRYPTION_KEY", _fernet_key())
1289
+ items, updates, snapshots = _history_update_fakes(
1290
+ monkeypatch, writer_error=RuntimeError("history unavailable"), writer_error_call=2,
1291
+ )
1292
+ with pytest.raises(RuntimeError, match="history unavailable"):
1293
+ await items.update_credential_field_value(
1294
+ "00000000-0000-0000-0000-000000000004", "synthetic-password-b", actor_id="00000000-0000-0000-0000-000000000002",
1295
+ audit_organization_id="00000000-0000-0000-0000-000000000003",
1296
+ )
1297
+ assert len(snapshots) == 2
1298
+ assert updates # The real transaction owns this statement and rolls it back on the propagated failure.
1299
+
1300
+
1301
+ @pytest.mark.asyncio
1302
+ async def test_inactive_password_never_enters_history_capture(monkeypatch):
1303
+ monkeypatch.setenv("CREDENTIALS_ENCRYPTION_KEY", _fernet_key())
1304
+ items, updates, snapshots = _history_update_fakes(monkeypatch)
1305
+ original = items.UserSecret
1306
+
1307
+ class _InactiveSecrets(original):
1308
+ @classmethod
1309
+ def filter(cls, **_kw):
1310
+ return _FakeQB([dict(_FIELD_RAW, id="00000000-0000-0000-0000-000000000004", field_key="password", editable=True, is_active=False, value_encrypted=_history_ciphertext())])
1311
+
1312
+ monkeypatch.setattr(items, "UserSecret", _InactiveSecrets)
1313
+ await items.update_credential_field_value(
1314
+ "00000000-0000-0000-0000-000000000004", "synthetic-password-b", actor_id="00000000-0000-0000-0000-000000000002",
1315
+ audit_organization_id="00000000-0000-0000-0000-000000000003",
1316
+ )
1317
+ assert snapshots == []
1318
+ assert updates
@@ -1,6 +0,0 @@
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.
File without changes
File without changes
File without changes
File without changes