matrx-orm 3.1.96__tar.gz → 3.1.97__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 (348) hide show
  1. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/PKG-INFO +2 -2
  2. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/__init__.py +14 -1
  3. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/catalog.py +12 -1
  4. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/async_db_manager.py +41 -1
  5. matrx_orm-3.1.97/matrx_orm/core/diagnostics.py +219 -0
  6. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/pyproject.toml +2 -2
  7. matrx_orm-3.1.96/matrx_orm/core/diagnostics.py +0 -115
  8. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/.gitignore +0 -0
  9. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/AGENTS.md +0 -0
  10. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/BASE_CLASS_METHODS.md +0 -0
  11. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/CLAUDE.md +0 -0
  12. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/MODEL_API.md +0 -0
  13. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/README.md +0 -0
  14. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/database/orm/extended/managers/ai_model_base.py +0 -0
  15. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/docs/migrations.md +0 -0
  16. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/FEATURE.md +0 -0
  17. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/MODULE_README.md +0 -0
  18. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/adapters/MODULE_README.md +0 -0
  19. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/adapters/__init__.py +0 -0
  20. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/adapters/async_postgresql.py +0 -0
  21. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/adapters/base_adapter.py +0 -0
  22. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/adapters/postgresql.py +0 -0
  23. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  24. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/adapters/supabase_adapter.py +0 -0
  25. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/admin/FEATURE.md +0 -0
  26. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/admin/__init__.py +0 -0
  27. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/admin/router.py +0 -0
  28. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/api/__init__.py +0 -0
  29. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/api/auth.py +0 -0
  30. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/api/config.py +0 -0
  31. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/api/handlers.py +0 -0
  32. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/api/protocol.py +0 -0
  33. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/api/server.py +0 -0
  34. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/associations.py +0 -0
  35. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/cache_debug.py +0 -0
  36. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/catalog_sql.py +0 -0
  37. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/client/MODULE_README.md +0 -0
  38. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/client/__init__.py +0 -0
  39. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/client/postgres_connection.py +0 -0
  40. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/client/postgrest.py +0 -0
  41. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/client/supabase_auth.py +0 -0
  42. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/client/supabase_config.py +0 -0
  43. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/client/supabase_manager.py +0 -0
  44. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  45. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/FEATURE.md +0 -0
  46. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/MODULE_README.md +0 -0
  47. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  48. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/TASKS.md +0 -0
  49. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/__init__.py +0 -0
  50. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/base.py +0 -0
  51. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/config.py +0 -0
  52. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/connections.py +0 -0
  53. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/db_function.py +0 -0
  54. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/expressions.py +0 -0
  55. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/extended.py +0 -0
  56. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/fields.py +0 -0
  57. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/introspection.py +0 -0
  58. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/listen.py +0 -0
  59. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/loop_filters.py +0 -0
  60. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/model_dto.py +0 -0
  61. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/model_view.py +0 -0
  62. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/one_database.py +0 -0
  63. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/paginator.py +0 -0
  64. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/pool_maintenance.py +0 -0
  65. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/pool_watch.py +0 -0
  66. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/pooler_role.py +0 -0
  67. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/pydantic_bridge.py +0 -0
  68. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/registry.py +0 -0
  69. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/relations.py +0 -0
  70. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/resilience.py +0 -0
  71. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/rls_session.py +0 -0
  72. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/signals.py +0 -0
  73. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/tls.py +0 -0
  74. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/transaction.py +0 -0
  75. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/types.py +0 -0
  76. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/core/write_queue.py +0 -0
  77. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/entity.py +0 -0
  78. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/error_handling.py +0 -0
  79. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/exceptions.py +0 -0
  80. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/extended/__init__.py +0 -0
  81. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/extended/app_error_handler.py +0 -0
  82. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/identity_rule.py +0 -0
  83. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/local_sqlite.py +0 -0
  84. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/middleware/__init__.py +0 -0
  85. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/middleware/base.py +0 -0
  86. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/MODULE_README.md +0 -0
  87. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/__init__.py +0 -0
  88. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/cli.py +0 -0
  89. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/ddl.py +0 -0
  90. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/diff.py +0 -0
  91. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/executor.py +0 -0
  92. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/integration.py +0 -0
  93. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/loader.py +0 -0
  94. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/operations.py +0 -0
  95. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/state.py +0 -0
  96. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/migrations/table_filter.py +0 -0
  97. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/FEATURE.md +0 -0
  98. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/MODULE_README.md +0 -0
  99. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/__init__.py +0 -0
  100. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/bulk_update_values.py +0 -0
  101. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/conflict.py +0 -0
  102. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/conflict_writes.py +0 -0
  103. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/create.py +0 -0
  104. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/db_functions.py +0 -0
  105. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/delete.py +0 -0
  106. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/dynamic_admin.py +0 -0
  107. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/dynamic_crud.py +0 -0
  108. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/graph_walk.py +0 -0
  109. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/latest_rows.py +0 -0
  110. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/queue_claim.py +0 -0
  111. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/read.py +0 -0
  112. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/relational_backfill.py +0 -0
  113. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/staging_load.py +0 -0
  114. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/operations/update.py +0 -0
  115. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/platform_db.py +0 -0
  116. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/py.typed +0 -0
  117. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/python_sql/MODULE_README.md +0 -0
  118. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/python_sql/__init__.py +0 -0
  119. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/python_sql/db_objects.py +0 -0
  120. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  121. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  122. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/query/FEATURE.md +0 -0
  123. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/query/MODULE_README.md +0 -0
  124. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/query/__init__.py +0 -0
  125. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/query/builder.py +0 -0
  126. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/query/executor.py +0 -0
  127. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/FEATURE.md +0 -0
  128. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  129. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/__init__.py +0 -0
  130. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/code_handler.py +0 -0
  131. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  132. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/columns.py +0 -0
  133. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/common.py +0 -0
  134. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/diff_preview.py +0 -0
  135. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  136. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/generator.py +0 -0
  137. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  138. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  139. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  140. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  141. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/package_wiring.py +0 -0
  142. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/relationships.py +0 -0
  143. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/runner.py +0 -0
  144. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/schema.py +0 -0
  145. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/schema_manager.py +0 -0
  146. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/tables.py +0 -0
  147. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/schema_builder/views.py +0 -0
  148. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/secrets_battery/FEATURE.md +0 -0
  149. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/secrets_battery/__init__.py +0 -0
  150. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/secrets_battery/attachments.py +0 -0
  151. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/secrets_battery/crypto.py +0 -0
  152. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/secrets_battery/items.py +0 -0
  153. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/secrets_battery/model.py +0 -0
  154. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/secrets_battery/org_service.py +0 -0
  155. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/secrets_battery/service.py +0 -0
  156. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/README.md +0 -0
  157. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/__init__.py +0 -0
  158. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/coalesce.py +0 -0
  159. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/dag.py +0 -0
  160. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/errors.py +0 -0
  161. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/fallback.py +0 -0
  162. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/flush.py +0 -0
  163. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/lifecycle.py +0 -0
  164. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/managed.py +0 -0
  165. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/op.py +0 -0
  166. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/reads.py +0 -0
  167. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/session.py +0 -0
  168. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/session/telemetry.py +0 -0
  169. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  170. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/sql_executor/__init__.py +0 -0
  171. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/sql_executor/executor.py +0 -0
  172. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/sql_executor/queries.py +0 -0
  173. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/sql_executor/registry.py +0 -0
  174. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/sql_executor/types.py +0 -0
  175. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/sql_executor/utils.py +0 -0
  176. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/state.py +0 -0
  177. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/utils/__init__.py +0 -0
  178. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/utils/sql_utils.py +0 -0
  179. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/matrx_orm/utils/type_converters.py +0 -0
  180. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/scripts/git-branches.sh +0 -0
  181. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/scripts/publish.sh +0 -0
  182. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/scripts/release.sh +0 -0
  183. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/MODULE_README.md +0 -0
  184. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/__init__.py +0 -0
  185. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/conftest.py +0 -0
  186. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/MODULE_README.md +0 -0
  187. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/__init__.py +0 -0
  188. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_admin_db_columns.py +0 -0
  189. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_admin_response_rendering.py +0 -0
  190. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_admin_search_negation.py +0 -0
  191. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_admin_search_sentinels.py +0 -0
  192. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_advisory_lock.py +0 -0
  193. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_agent_message.py +0 -0
  194. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_api_auth.py +0 -0
  195. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_api_config.py +0 -0
  196. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_api_handlers.py +0 -0
  197. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_api_protocol.py +0 -0
  198. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_array_agg_order_by.py +0 -0
  199. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_associations.py +0 -0
  200. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_bulk_hydration_offload.py +0 -0
  201. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_bulk_update_by_pk.py +0 -0
  202. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  203. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_cache_debug.py +0 -0
  204. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_call_function.py +0 -0
  205. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_case_when.py +0 -0
  206. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_catalog.py +0 -0
  207. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_composite_pk_filter.py +0 -0
  208. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_composite_pk_write_paths.py +0 -0
  209. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_config.py +0 -0
  210. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_conflict_target.py +0 -0
  211. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_conflict_writes.py +0 -0
  212. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_connection_codecs.py +0 -0
  213. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_connection_poison_guard.py +0 -0
  214. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_connection_primitives.py +0 -0
  215. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_count_composite_distinct.py +0 -0
  216. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_db_function_field.py +0 -0
  217. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_ddl_generator.py +0 -0
  218. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_derived_table.py +0 -0
  219. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_dirty_tracking.py +0 -0
  220. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_dynamic_crud.py +0 -0
  221. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_exceptions.py +0 -0
  222. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  223. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_expression_primitives.py +0 -0
  224. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_fields.py +0 -0
  225. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  226. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_from_alias.py +0 -0
  227. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_fts.py +0 -0
  228. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_func_expression_args.py +0 -0
  229. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_graph_walk.py +0 -0
  230. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_having_annotate_alias.py +0 -0
  231. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  232. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_introspect_rls_policies.py +0 -0
  233. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_join_and_aggregate.py +0 -0
  234. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_latest_rows.py +0 -0
  235. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_listen_notify.py +0 -0
  236. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_local_sqlite.py +0 -0
  237. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_managed_write_guard.py +0 -0
  238. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_matrx_entity.py +0 -0
  239. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_migration_diff_types.py +0 -0
  240. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_migration_loader.py +0 -0
  241. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_model_instance.py +0 -0
  242. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_model_meta.py +0 -0
  243. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_optimistic_concurrency.py +0 -0
  244. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_order_by_expression.py +0 -0
  245. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_parameter_name_hydration.py +0 -0
  246. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_patch_jsonb_path.py +0 -0
  247. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_pool_holder_census.py +0 -0
  248. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_pool_loop_recreation.py +0 -0
  249. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_pool_maintenance.py +0 -0
  250. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_postgrest_filters.py +0 -0
  251. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_pydantic_bridge.py +0 -0
  252. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_query_builder.py +0 -0
  253. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_query_error_detail.py +0 -0
  254. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_query_executor_sql.py +0 -0
  255. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_query_timeout.py +0 -0
  256. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  257. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_queue_claim.py +0 -0
  258. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_registry.py +0 -0
  259. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_registry_multi_database.py +0 -0
  260. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_registry_multi_schema.py +0 -0
  261. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_relational_backfill.py +0 -0
  262. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_relations.py +0 -0
  263. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_resilience.py +0 -0
  264. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_rls_session_scope.py +0 -0
  265. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_schema_exists.py +0 -0
  266. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_session_advisory_lock.py +0 -0
  267. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_state_cache.py +0 -0
  268. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_subquery_filter_raw.py +0 -0
  269. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_subquery_in_filter.py +0 -0
  270. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_subquery_nested_expressions.py +0 -0
  271. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_supabase_auth.py +0 -0
  272. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_supabase_config.py +0 -0
  273. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_tls.py +0 -0
  274. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_typed_join_projection.py +0 -0
  275. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_typed_predicates.py +0 -0
  276. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_update_case_expression.py +0 -0
  277. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_update_subquery.py +0 -0
  278. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_update_with_rebase.py +0 -0
  279. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_upsert_default_leak.py +0 -0
  280. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_upsert_with_conflict.py +0 -0
  281. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_values_jsonb_decode.py +0 -0
  282. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_watched_lifecycle.py +0 -0
  283. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level1/test_write_retry_semantics.py +0 -0
  284. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/MODULE_README.md +0 -0
  285. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/__init__.py +0 -0
  286. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/conftest.py +0 -0
  287. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_bulk_ops.py +0 -0
  288. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_cache_integration.py +0 -0
  289. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_crud.py +0 -0
  290. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_foreign_keys.py +0 -0
  291. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_m2m.py +0 -0
  292. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_manager.py +0 -0
  293. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_migrations_live.py +0 -0
  294. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_query_execution.py +0 -0
  295. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/level2/test_schema_diff.py +0 -0
  296. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/live_pooler_role.py +0 -0
  297. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project/.env.example +0 -0
  298. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project/README.md +0 -0
  299. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project/__init__.py +0 -0
  300. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project/generate.py +0 -0
  301. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project/generated/.gitkeep +0 -0
  302. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project/matrx_orm.yaml +0 -0
  303. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project/test_schema_generation.py +0 -0
  304. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project_desktop/.env.example +0 -0
  305. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project_desktop/README.md +0 -0
  306. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project_desktop/__init__.py +0 -0
  307. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project_desktop/client_example.py +0 -0
  308. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project_desktop/client_example.ts +0 -0
  309. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  310. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/sample_project_desktop/server.py +0 -0
  311. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/entity_tests.py +0 -0
  312. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_base_generation.py +0 -0
  313. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  314. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_composite_unique_generation.py +0 -0
  315. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_dto_identity_generation.py +0 -0
  316. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_entity_capability_generation.py +0 -0
  317. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_enum_ordering.py +0 -0
  318. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_generate_schema.py +0 -0
  319. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_junction_analysis.py +0 -0
  320. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_multi_schema_output.py +0 -0
  321. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_sql_expression_defaults.py +0 -0
  322. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_table_relationships_query.py +0 -0
  323. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema/test_view_generation.py +0 -0
  324. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema_builder/test_array_default_parsing.py +0 -0
  325. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  326. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  327. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/__init__.py +0 -0
  328. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/conftest.py +0 -0
  329. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_cache_no_downgrade.py +0 -0
  330. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_capture_poison_proof.py +0 -0
  331. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_coalesce.py +0 -0
  332. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_dag.py +0 -0
  333. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_disk_spill_fallback.py +0 -0
  334. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_flush_individual_fallback.py +0 -0
  335. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_governed_write.py +0 -0
  336. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_managed.py +0 -0
  337. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_op_enqueue_site.py +0 -0
  338. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_reads.py +0 -0
  339. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/session/test_session.py +0 -0
  340. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_database_name_alias.py +0 -0
  341. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_db_functions.py +0 -0
  342. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_model_cls_refactor.py +0 -0
  343. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  344. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_one_database.py +0 -0
  345. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_pooler_role_guard.py +0 -0
  346. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_secrets_battery.py +0 -0
  347. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_sql_param_casts.py +0 -0
  348. {matrx_orm-3.1.96 → matrx_orm-3.1.97}/tests/test_sync_twins_guard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.96
3
+ Version: 3.1.97
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
@@ -25,7 +25,7 @@ Requires-Python: >=3.13
25
25
  Requires-Dist: asyncpg>=0.31.0
26
26
  Requires-Dist: cryptography>=43.0
27
27
  Requires-Dist: gitpython>=3.1.40
28
- Requires-Dist: matrx-utils>=2.0.20
28
+ Requires-Dist: matrx-utils>=2.0.21
29
29
  Requires-Dist: psycopg-pool>=3.2.5
30
30
  Requires-Dist: psycopg[binary]>=3.2.5
31
31
  Requires-Dist: python-dotenv>=1.0
@@ -44,7 +44,15 @@ from .platform_db import (
44
44
  register_platform_db,
45
45
  )
46
46
  from .local_sqlite import SQLiteValue, check_local_sqlite_integrity, read_local_sqlite_rows
47
- from .core.diagnostics import collect_pool_diagnostics, log_pool_diagnostics
47
+ from .core.diagnostics import (
48
+ LONG_TRANSACTION_SECONDS,
49
+ collect_pool_diagnostics,
50
+ describe_transaction,
51
+ describe_transactions,
52
+ log_pool_diagnostics,
53
+ long_transactions,
54
+ long_transactions_sql,
55
+ )
48
56
  from .core.introspection import introspect_columns, introspect_rls_policies, schema_exists
49
57
  from .core.one_database import OneDatabaseReport, PoolIdentity, audit_one_database
50
58
  from .core.listen import notify, ping
@@ -380,6 +388,11 @@ __all__ = [
380
388
  # ── Diagnostics ────────────────────────────────────────────────────────────
381
389
  "collect_pool_diagnostics",
382
390
  "log_pool_diagnostics",
391
+ "LONG_TRANSACTION_SECONDS",
392
+ "long_transactions",
393
+ "long_transactions_sql",
394
+ "describe_transaction",
395
+ "describe_transactions",
383
396
  "install_asyncpg_idle_close_filter",
384
397
  # ── DB-side function calls (RPC) ──────────────────────────────────────────
385
398
  "call_function",
@@ -452,8 +452,19 @@ async def org_backstop_coverage(database: str) -> list[dict[str, Any]]:
452
452
  EXISTS (SELECT 1 FROM pg_trigger t
453
453
  JOIN pg_class cl ON cl.oid=t.tgrelid
454
454
  JOIN pg_namespace n ON n.oid=cl.relnamespace
455
+ JOIN pg_proc bp ON bp.oid=t.tgfoid
455
456
  WHERE n.nspname=c.table_schema AND cl.relname=c.table_name
456
- AND t.tgname='_stamp_org_default' AND NOT t.tgisinternal) AS has_backstop
457
+ AND NOT t.tgisinternal
458
+ -- Match the FUNCTION, not the trigger name: db-rules FEATURE.md
459
+ -- §2 names THREE legal org backstops, and only the first is
460
+ -- conventionally called `_stamp_org_default`. A child entity's
461
+ -- backstop is `platform.inherit_org_from_parent` (trigger
462
+ -- `_inherit_org`), and `plan` uses `_stamp_from_node`. Keying on
463
+ -- the trigger name reported correctly-backstopped children as
464
+ -- DRIFT (D232 §C, 2026-08-21).
465
+ AND bp.proname IN ('_stamp_org_default',
466
+ 'inherit_org_from_parent',
467
+ '_stamp_from_node')) AS has_backstop
457
468
  FROM pg_catalog_columns c
458
469
  JOIN pg_catalog_tables tb
459
470
  ON tb.table_schema=c.table_schema AND tb.table_name=c.table_name
@@ -113,6 +113,30 @@ POOL_RELEASE_TIMEOUT_SECONDS = 10.0
113
113
  READ_TIMEOUT_RETRIES = 1 # extra attempts for a timed-out read; total = 1 + this
114
114
  READ_TIMEOUT_RETRY_BACKOFF = 0.25 # seconds before the read retry
115
115
 
116
+ # ── idle-in-transaction ceiling (the platform-wide DDL blocker) ───────────────
117
+ # A session that BEGINs and then goes quiet is the single most expensive thing a
118
+ # client can do to a shared Postgres: it holds every lock it took (so DDL —
119
+ # CREATE POLICY, ALTER TABLE — queues behind it platform-wide) AND it pins the
120
+ # cluster xmin horizon, which stops autovacuum from reclaiming dead tuples
121
+ # ANYWHERE in the database, not just on the tables it touched.
122
+ #
123
+ # The incident (2026-08-21, feedback c72a32ac): one session sat
124
+ # `idle in transaction` for 18+ minutes mid-bulk-read of the ops log tables.
125
+ # Every `CREATE POLICY` against the live DB blocked to lock_timeout while plain
126
+ # DDL on the same brand-new table succeeded in <0.2s, and each agent applying a
127
+ # migration was told by the applier to "fix the SQL" — which was fine.
128
+ #
129
+ # Postgres will end such a session for us if we ask. This is the ceiling for
130
+ # every pooled ORM connection: nothing the ORM does legitimately sits idle
131
+ # mid-transaction for a minute. Note what this does NOT kill — a long-RUNNING
132
+ # statement is `active`, and this timeout only counts IDLE time inside an open
133
+ # transaction, so a 10-minute bulk INSERT is untouched. A caller with a genuine
134
+ # need overrides it per-database via `server_settings`.
135
+ #
136
+ # A threshold → a CAPS constant, never an env var (an invisible prod toggle is
137
+ # the failure class this repo bans).
138
+ IDLE_IN_TRANSACTION_TIMEOUT_MS = 60_000 # 60s
139
+
116
140
  # One idle holder is probed per tick. The interval adapts so every currently
117
141
  # connected holder is touched within this sweep window, below the default 60s
118
142
  # transaction-pool idle lifetime. A degraded pool still attempts at most one
@@ -497,6 +521,19 @@ class AsyncDatabaseManager:
497
521
  # Disable globally — it's the standard recommendation
498
522
  # for OLTP-style workloads.
499
523
  "jit": "off",
524
+ # A session that opens a transaction and then stops
525
+ # talking holds its locks and pins the cluster-wide
526
+ # xmin horizon until it comes back — blocking DDL for
527
+ # everyone and stopping autovacuum EVERYWHERE, not just
528
+ # on the tables it touched. Postgres kills such a
529
+ # session for us if we ask it to, and nothing this ORM
530
+ # does legitimately sits idle mid-transaction for
531
+ # minutes (a long-RUNNING statement is `active`, which
532
+ # this timeout does not count). See IDLE_IN_TRANSACTION
533
+ # _TIMEOUT_MS for the incident that earned it.
534
+ "idle_in_transaction_session_timeout": str(
535
+ IDLE_IN_TRANSACTION_TIMEOUT_MS
536
+ ),
500
537
  }
501
538
  server_settings.update(dict(config.get("server_settings") or {}))
502
539
 
@@ -528,7 +565,7 @@ class AsyncDatabaseManager:
528
565
  logger.info(
529
566
  "[matrx_orm] Pool created for '%s' "
530
567
  "(mode=%s, port=%s, min=%d, max=%d, idle_lifetime=%.1fs, "
531
- "jit=%s)",
568
+ "jit=%s, idle_in_transaction_timeout=%sms)",
532
569
  config_name,
533
570
  effective_mode,
534
571
  config.get("port"),
@@ -536,6 +573,9 @@ class AsyncDatabaseManager:
536
573
  config.get("pool_max", 30),
537
574
  max_inactive_connection_lifetime,
538
575
  server_settings.get("jit", "default"),
576
+ server_settings.get(
577
+ "idle_in_transaction_session_timeout", "default"
578
+ ),
539
579
  )
540
580
 
541
581
  if config.get("write_queue_enabled", True):
@@ -0,0 +1,219 @@
1
+ """Pool diagnostics — surface the pool config and live server-side limits.
2
+
3
+ Call ``log_pool_diagnostics(config_name)`` once at app startup (after the
4
+ first query) to log:
5
+
6
+ - The resolved pool mode (transaction vs session) and effective port.
7
+ - The pool ceiling (min/max) the app is configured for.
8
+ - The Postgres server's ``max_connections`` setting.
9
+ - The current per-pool connection counts (``pool.get_size()``,
10
+ ``pool.get_idle_size()``).
11
+
12
+ Surfacing these at startup makes Supavisor saturation issues debuggable
13
+ *before* they happen — a misconfigured ``pool_max`` is visible in the
14
+ boot log instead of as a 502 in production.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import logging
20
+ from typing import Any
21
+
22
+ from matrx_orm.core.config import get_database_config, resolve_pool_mode
23
+
24
+ logger = logging.getLogger("matrx_orm.diagnostics")
25
+
26
+
27
+ async def collect_pool_diagnostics(config_name: str) -> dict[str, Any]:
28
+ """Return a dict of diagnostic info for ``config_name``.
29
+
30
+ Creates the pool if it doesn't exist yet (so the diagnostics also serve
31
+ as a cheap "warm up" probe). Safe to call from any async context.
32
+ """
33
+ from matrx_orm.core.async_db_manager import AsyncDatabaseManager
34
+
35
+ config = get_database_config(config_name)
36
+ pool = await AsyncDatabaseManager.get_pool(config_name)
37
+
38
+ server_max_connections: str | None = None
39
+ server_version: str | None = None
40
+ inet_port: str | None = None
41
+ application_name: str | None = None
42
+
43
+ try:
44
+ async with AsyncDatabaseManager.get_connection(config_name, timeout=5.0) as conn:
45
+ server_max_connections = await conn.fetchval(
46
+ "SELECT current_setting('max_connections')"
47
+ )
48
+ server_version = await conn.fetchval("SELECT current_setting('server_version')")
49
+ inet_port = await conn.fetchval("SELECT current_setting('port', true)")
50
+ application_name = await conn.fetchval(
51
+ "SELECT current_setting('application_name', true)"
52
+ )
53
+ except Exception as exc:
54
+ logger.warning(
55
+ "[matrx_orm] Diagnostics probe failed for '%s': %s",
56
+ config_name,
57
+ exc,
58
+ )
59
+
60
+ effective_mode = resolve_pool_mode(config)
61
+ configured_idle_lifetime = config.get("max_inactive_connection_lifetime")
62
+ if configured_idle_lifetime is None:
63
+ idle_lifetime_effective = 60.0 if effective_mode == "transaction" else 300.0
64
+ idle_lifetime_source = "default"
65
+ else:
66
+ idle_lifetime_effective = float(configured_idle_lifetime)
67
+ idle_lifetime_source = "configured"
68
+
69
+ return {
70
+ "config_name": config_name,
71
+ "host": config.get("host"),
72
+ "port": str(config.get("port")),
73
+ "database": config.get("database_name"),
74
+ "user": config.get("user"),
75
+ "pool_mode_configured": config.get("pool_mode"),
76
+ "pool_mode_effective": effective_mode,
77
+ "pool_min": config.get("pool_min"),
78
+ "pool_max": config.get("pool_max"),
79
+ "max_inactive_connection_lifetime": idle_lifetime_effective,
80
+ "max_inactive_connection_lifetime_source": idle_lifetime_source,
81
+ "current_pool_size": pool.get_size(),
82
+ "current_pool_idle": pool.get_idle_size(),
83
+ "server_version": server_version,
84
+ "server_max_connections": server_max_connections,
85
+ "server_inet_port": inet_port,
86
+ "application_name": application_name,
87
+ }
88
+
89
+
90
+ async def log_pool_diagnostics(config_name: str) -> None:
91
+ """Emit a single human-readable INFO log line summarizing the pool state."""
92
+ info = await collect_pool_diagnostics(config_name)
93
+ # INFO, not WARNING — a healthy pool is not a warning. Surfaces only when
94
+ # the app raises its log level; saturation problems fail loudly elsewhere.
95
+ logger.info(
96
+ "[matrx_orm] Pool diagnostics '%s' — host=%s:%s db=%s user=%s | "
97
+ "mode=%s (configured=%s) min=%s max=%s idle_lifetime=%.0fs(%s) "
98
+ "now=%s/%s | server pg=%s max_connections=%s app=%s",
99
+ info["config_name"],
100
+ info["host"],
101
+ info["port"],
102
+ info["database"],
103
+ info["user"],
104
+ info["pool_mode_effective"],
105
+ info["pool_mode_configured"],
106
+ info["pool_min"],
107
+ info["pool_max"],
108
+ info["max_inactive_connection_lifetime"],
109
+ info["max_inactive_connection_lifetime_source"],
110
+ info["current_pool_idle"],
111
+ info["current_pool_size"],
112
+ info["server_version"],
113
+ info["server_max_connections"],
114
+ info["application_name"],
115
+ )
116
+
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # Long-lived transactions — the platform-wide DDL blocker, made nameable.
120
+ #
121
+ # A session that holds a transaction open holds its locks (DDL platform-wide
122
+ # queues behind it) and pins the cluster xmin horizon (autovacuum stops
123
+ # reclaiming dead tuples EVERYWHERE, not just on the tables it touched). When
124
+ # it is also `idle in transaction`, no work is even being done for the price.
125
+ #
126
+ # The incident (2026-08-21, feedback c72a32ac): one session sat idle in
127
+ # transaction for 18+ minutes; every `CREATE POLICY` against the live database
128
+ # blocked to lock_timeout, and the migration applier told each agent to "fix
129
+ # the SQL" — which was fine. Nothing named the blocker, so the diagnosis cost
130
+ # an hour of forensics that this primitive answers in one call.
131
+ #
132
+ # Reusable on purpose: the async form serves in-process guards, and
133
+ # ``long_transactions_sql`` is exported so SYNC ops tooling (psycopg migration
134
+ # appliers, watches) runs the exact same query with no driver coupling — it
135
+ # takes no bind parameters, so every paramstyle can execute it verbatim.
136
+ # ---------------------------------------------------------------------------
137
+
138
+ #: A transaction open at least this long is worth naming in an error message.
139
+ LONG_TRANSACTION_SECONDS = 120.0
140
+
141
+
142
+ def long_transactions_sql(min_seconds: float = LONG_TRANSACTION_SECONDS) -> str:
143
+ """SQL naming every transaction open longer than ``min_seconds``.
144
+
145
+ Returns one row per session with a ``category`` of ``idle_in_transaction``
146
+ (open and doing nothing — always pathological) or ``long_active`` (open and
147
+ working — legitimate for a bulk job, still a blocker for DDL). Takes no
148
+ bind parameters: ``min_seconds`` is a float this code controls and is
149
+ embedded as a literal, so the same string runs under asyncpg, psycopg, or
150
+ psql unchanged.
151
+ """
152
+ seconds = float(min_seconds)
153
+ return f"""
154
+ SELECT pid,
155
+ CASE WHEN state LIKE 'idle in transaction%'
156
+ THEN 'idle_in_transaction' ELSE 'long_active' END AS category,
157
+ usename,
158
+ application_name,
159
+ client_addr::text AS client_addr,
160
+ state,
161
+ wait_event_type,
162
+ wait_event,
163
+ round(EXTRACT(EPOCH FROM (now() - xact_start))::numeric, 1) AS xact_seconds,
164
+ round(EXTRACT(EPOCH FROM (now() - state_change))::numeric, 1) AS state_seconds,
165
+ to_char(xact_start, 'YYYY-MM-DD HH24:MI:SS TZ') AS xact_start,
166
+ left(regexp_replace(query, '\\s+', ' ', 'g'), 300) AS query
167
+ FROM pg_stat_activity
168
+ WHERE datname = current_database()
169
+ AND pid <> pg_backend_pid()
170
+ AND xact_start IS NOT NULL
171
+ AND backend_type = 'client backend'
172
+ AND now() - xact_start > interval '{seconds} seconds'
173
+ ORDER BY xact_start
174
+ LIMIT 50
175
+ """
176
+
177
+
178
+ async def long_transactions(
179
+ config_name: str,
180
+ *,
181
+ min_seconds: float = LONG_TRANSACTION_SECONDS,
182
+ connection: Any | None = None,
183
+ ) -> list[dict[str, Any]]:
184
+ """Every transaction on this database open longer than ``min_seconds``.
185
+
186
+ Read-only. Never raises for the caller: a probe that cannot run returns an
187
+ empty list and logs, because this is diagnostics — it must never be the
188
+ reason a guard or a migration applier crashes.
189
+ """
190
+ sql = long_transactions_sql(min_seconds)
191
+ try:
192
+ if connection is not None:
193
+ rows = await connection.fetch(sql)
194
+ else:
195
+ from matrx_orm.core.async_db_manager import AsyncDatabaseManager
196
+
197
+ async with AsyncDatabaseManager.get_connection(config_name, timeout=10.0) as conn:
198
+ rows = await conn.fetch(sql)
199
+ except Exception as exc:
200
+ logger.warning("[matrx_orm] long_transactions probe failed: %s", exc)
201
+ return []
202
+ return [dict(row) for row in rows]
203
+
204
+
205
+ def describe_transaction(row: dict[str, Any]) -> str:
206
+ """One dense line naming a blocker: who it is, how long, and its last query."""
207
+ return (
208
+ f"pid={row.get('pid')} {row.get('category')} "
209
+ f"xact_age={row.get('xact_seconds')}s state={row.get('state')!r} "
210
+ f"user={row.get('usename')!r} app={row.get('application_name')!r} "
211
+ f"client={row.get('client_addr')} "
212
+ f"wait={row.get('wait_event_type')}/{row.get('wait_event')} "
213
+ f"last_query={row.get('query')!r}"
214
+ )
215
+
216
+
217
+ def describe_transactions(rows: list[dict[str, Any]]) -> str:
218
+ """Multi-line blocker report, newest last. Empty string when there are none."""
219
+ return "\n".join(f" - {describe_transaction(r)}" for r in rows)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.1.96"
3
+ version = "3.1.97"
4
4
  description = "Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -41,7 +41,7 @@ dependencies = [
41
41
  "gitpython>=3.1.40",
42
42
  "psycopg[binary]>=3.2.5",
43
43
  "psycopg-pool>=3.2.5",
44
- "matrx-utils>=2.0.20",
44
+ "matrx-utils>=2.0.21",
45
45
  "pyyaml>=6.0.3",
46
46
  "python-dotenv>=1.0",
47
47
  "cryptography>=43.0",
@@ -1,115 +0,0 @@
1
- """Pool diagnostics — surface the pool config and live server-side limits.
2
-
3
- Call ``log_pool_diagnostics(config_name)`` once at app startup (after the
4
- first query) to log:
5
-
6
- - The resolved pool mode (transaction vs session) and effective port.
7
- - The pool ceiling (min/max) the app is configured for.
8
- - The Postgres server's ``max_connections`` setting.
9
- - The current per-pool connection counts (``pool.get_size()``,
10
- ``pool.get_idle_size()``).
11
-
12
- Surfacing these at startup makes Supavisor saturation issues debuggable
13
- *before* they happen — a misconfigured ``pool_max`` is visible in the
14
- boot log instead of as a 502 in production.
15
- """
16
-
17
- from __future__ import annotations
18
-
19
- import logging
20
- from typing import Any
21
-
22
- from matrx_orm.core.config import get_database_config, resolve_pool_mode
23
-
24
- logger = logging.getLogger("matrx_orm.diagnostics")
25
-
26
-
27
- async def collect_pool_diagnostics(config_name: str) -> dict[str, Any]:
28
- """Return a dict of diagnostic info for ``config_name``.
29
-
30
- Creates the pool if it doesn't exist yet (so the diagnostics also serve
31
- as a cheap "warm up" probe). Safe to call from any async context.
32
- """
33
- from matrx_orm.core.async_db_manager import AsyncDatabaseManager
34
-
35
- config = get_database_config(config_name)
36
- pool = await AsyncDatabaseManager.get_pool(config_name)
37
-
38
- server_max_connections: str | None = None
39
- server_version: str | None = None
40
- inet_port: str | None = None
41
- application_name: str | None = None
42
-
43
- try:
44
- async with AsyncDatabaseManager.get_connection(config_name, timeout=5.0) as conn:
45
- server_max_connections = await conn.fetchval(
46
- "SELECT current_setting('max_connections')"
47
- )
48
- server_version = await conn.fetchval("SELECT current_setting('server_version')")
49
- inet_port = await conn.fetchval("SELECT current_setting('port', true)")
50
- application_name = await conn.fetchval(
51
- "SELECT current_setting('application_name', true)"
52
- )
53
- except Exception as exc:
54
- logger.warning(
55
- "[matrx_orm] Diagnostics probe failed for '%s': %s",
56
- config_name,
57
- exc,
58
- )
59
-
60
- effective_mode = resolve_pool_mode(config)
61
- configured_idle_lifetime = config.get("max_inactive_connection_lifetime")
62
- if configured_idle_lifetime is None:
63
- idle_lifetime_effective = 60.0 if effective_mode == "transaction" else 300.0
64
- idle_lifetime_source = "default"
65
- else:
66
- idle_lifetime_effective = float(configured_idle_lifetime)
67
- idle_lifetime_source = "configured"
68
-
69
- return {
70
- "config_name": config_name,
71
- "host": config.get("host"),
72
- "port": str(config.get("port")),
73
- "database": config.get("database_name"),
74
- "user": config.get("user"),
75
- "pool_mode_configured": config.get("pool_mode"),
76
- "pool_mode_effective": effective_mode,
77
- "pool_min": config.get("pool_min"),
78
- "pool_max": config.get("pool_max"),
79
- "max_inactive_connection_lifetime": idle_lifetime_effective,
80
- "max_inactive_connection_lifetime_source": idle_lifetime_source,
81
- "current_pool_size": pool.get_size(),
82
- "current_pool_idle": pool.get_idle_size(),
83
- "server_version": server_version,
84
- "server_max_connections": server_max_connections,
85
- "server_inet_port": inet_port,
86
- "application_name": application_name,
87
- }
88
-
89
-
90
- async def log_pool_diagnostics(config_name: str) -> None:
91
- """Emit a single human-readable INFO log line summarizing the pool state."""
92
- info = await collect_pool_diagnostics(config_name)
93
- # INFO, not WARNING — a healthy pool is not a warning. Surfaces only when
94
- # the app raises its log level; saturation problems fail loudly elsewhere.
95
- logger.info(
96
- "[matrx_orm] Pool diagnostics '%s' — host=%s:%s db=%s user=%s | "
97
- "mode=%s (configured=%s) min=%s max=%s idle_lifetime=%.0fs(%s) "
98
- "now=%s/%s | server pg=%s max_connections=%s app=%s",
99
- info["config_name"],
100
- info["host"],
101
- info["port"],
102
- info["database"],
103
- info["user"],
104
- info["pool_mode_effective"],
105
- info["pool_mode_configured"],
106
- info["pool_min"],
107
- info["pool_max"],
108
- info["max_inactive_connection_lifetime"],
109
- info["max_inactive_connection_lifetime_source"],
110
- info["current_pool_idle"],
111
- info["current_pool_size"],
112
- info["server_version"],
113
- info["server_max_connections"],
114
- info["application_name"],
115
- )
File without changes
File without changes
File without changes
File without changes
File without changes