matrx-orm 3.1.113__tar.gz → 3.1.115__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 (351) hide show
  1. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/PKG-INFO +1 -1
  2. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/FEATURE.md +24 -1
  3. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/lifecycle.py +42 -26
  4. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/pyproject.toml +1 -1
  5. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_watched_lifecycle.py +64 -1
  6. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/.gitignore +0 -0
  7. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/AGENTS.md +0 -0
  8. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/BASE_CLASS_METHODS.md +0 -0
  9. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/CLAUDE.md +0 -0
  10. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/MODEL_API.md +0 -0
  11. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/README.md +0 -0
  12. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/database/orm/extended/managers/ai_model_base.py +0 -0
  13. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/docs/migrations.md +0 -0
  14. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/MODULE_README.md +0 -0
  15. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/__init__.py +0 -0
  16. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/adapters/MODULE_README.md +0 -0
  17. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/adapters/__init__.py +0 -0
  18. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/adapters/async_postgresql.py +0 -0
  19. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/adapters/base_adapter.py +0 -0
  20. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/adapters/postgresql.py +0 -0
  21. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  22. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/adapters/supabase_adapter.py +0 -0
  23. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/admin/FEATURE.md +0 -0
  24. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/admin/__init__.py +0 -0
  25. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/admin/router.py +0 -0
  26. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/api/__init__.py +0 -0
  27. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/api/auth.py +0 -0
  28. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/api/config.py +0 -0
  29. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/api/handlers.py +0 -0
  30. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/api/protocol.py +0 -0
  31. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/api/server.py +0 -0
  32. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/associations.py +0 -0
  33. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/cache_debug.py +0 -0
  34. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/catalog.py +0 -0
  35. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/catalog_sql.py +0 -0
  36. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/client/MODULE_README.md +0 -0
  37. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/client/__init__.py +0 -0
  38. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/client/postgres_connection.py +0 -0
  39. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/client/postgrest.py +0 -0
  40. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/client/supabase_auth.py +0 -0
  41. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/client/supabase_config.py +0 -0
  42. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/client/supabase_manager.py +0 -0
  43. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  44. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/FEATURE.md +0 -0
  45. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/MODULE_README.md +0 -0
  46. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  47. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/TASKS.md +0 -0
  48. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/__init__.py +0 -0
  49. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/async_db_manager.py +0 -0
  50. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/base.py +0 -0
  51. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/config.py +0 -0
  52. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/connections.py +0 -0
  53. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/db_function.py +0 -0
  54. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/diagnostics.py +0 -0
  55. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/expressions.py +0 -0
  56. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/extended.py +0 -0
  57. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/fields.py +0 -0
  58. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/introspection.py +0 -0
  59. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/listen.py +0 -0
  60. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/loop_filters.py +0 -0
  61. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/model_dto.py +0 -0
  62. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/model_view.py +0 -0
  63. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/one_database.py +0 -0
  64. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/paginator.py +0 -0
  65. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/pool_maintenance.py +0 -0
  66. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/pool_watch.py +0 -0
  67. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/pooler_role.py +0 -0
  68. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/pydantic_bridge.py +0 -0
  69. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/registry.py +0 -0
  70. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/relations.py +0 -0
  71. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/resilience.py +0 -0
  72. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/rls_session.py +0 -0
  73. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/signals.py +0 -0
  74. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/tls.py +0 -0
  75. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/transaction.py +0 -0
  76. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/types.py +0 -0
  77. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/core/write_queue.py +0 -0
  78. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/entity.py +0 -0
  79. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/error_handling.py +0 -0
  80. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/exceptions.py +0 -0
  81. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/extended/__init__.py +0 -0
  82. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/extended/app_error_handler.py +0 -0
  83. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/identity_rule.py +0 -0
  84. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/local_sqlite.py +0 -0
  85. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/middleware/__init__.py +0 -0
  86. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/middleware/base.py +0 -0
  87. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/MODULE_README.md +0 -0
  88. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/__init__.py +0 -0
  89. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/cli.py +0 -0
  90. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/ddl.py +0 -0
  91. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/diff.py +0 -0
  92. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/executor.py +0 -0
  93. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/integration.py +0 -0
  94. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/loader.py +0 -0
  95. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/operations.py +0 -0
  96. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/state.py +0 -0
  97. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/migrations/table_filter.py +0 -0
  98. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/FEATURE.md +0 -0
  99. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/MODULE_README.md +0 -0
  100. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/__init__.py +0 -0
  101. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/bulk_update_values.py +0 -0
  102. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/conflict.py +0 -0
  103. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/conflict_writes.py +0 -0
  104. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/create.py +0 -0
  105. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/db_functions.py +0 -0
  106. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/delete.py +0 -0
  107. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/dynamic_admin.py +0 -0
  108. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/dynamic_crud.py +0 -0
  109. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/graph_walk.py +0 -0
  110. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/latest_rows.py +0 -0
  111. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/queue_claim.py +0 -0
  112. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/read.py +0 -0
  113. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/relational_backfill.py +0 -0
  114. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/staging_load.py +0 -0
  115. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/operations/update.py +0 -0
  116. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/platform_db.py +0 -0
  117. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/py.typed +0 -0
  118. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/python_sql/MODULE_README.md +0 -0
  119. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/python_sql/__init__.py +0 -0
  120. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/python_sql/db_objects.py +0 -0
  121. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  122. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  123. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/query/FEATURE.md +0 -0
  124. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/query/MODULE_README.md +0 -0
  125. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/query/__init__.py +0 -0
  126. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/query/builder.py +0 -0
  127. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/query/executor.py +0 -0
  128. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/FEATURE.md +0 -0
  129. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  130. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/__init__.py +0 -0
  131. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/code_handler.py +0 -0
  132. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  133. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/columns.py +0 -0
  134. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/common.py +0 -0
  135. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/diff_preview.py +0 -0
  136. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  137. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/generator.py +0 -0
  138. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  139. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  140. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  141. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  142. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/package_wiring.py +0 -0
  143. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/relationships.py +0 -0
  144. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/runner.py +0 -0
  145. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/schema.py +0 -0
  146. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/schema_manager.py +0 -0
  147. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/tables.py +0 -0
  148. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/schema_builder/views.py +0 -0
  149. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/FEATURE.md +0 -0
  150. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/__init__.py +0 -0
  151. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/attachments.py +0 -0
  152. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/crypto.py +0 -0
  153. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  154. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/items.py +0 -0
  155. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/model.py +0 -0
  156. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/org_service.py +0 -0
  157. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/secrets_battery/service.py +0 -0
  158. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/README.md +0 -0
  159. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/__init__.py +0 -0
  160. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/coalesce.py +0 -0
  161. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/dag.py +0 -0
  162. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/errors.py +0 -0
  163. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/fallback.py +0 -0
  164. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/flush.py +0 -0
  165. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/managed.py +0 -0
  166. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/op.py +0 -0
  167. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/reads.py +0 -0
  168. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/session.py +0 -0
  169. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/session/telemetry.py +0 -0
  170. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  171. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/sql_executor/__init__.py +0 -0
  172. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/sql_executor/executor.py +0 -0
  173. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/sql_executor/queries.py +0 -0
  174. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/sql_executor/registry.py +0 -0
  175. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/sql_executor/types.py +0 -0
  176. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/sql_executor/utils.py +0 -0
  177. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/state.py +0 -0
  178. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/utils/__init__.py +0 -0
  179. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/utils/sql_utils.py +0 -0
  180. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/matrx_orm/utils/type_converters.py +0 -0
  181. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/scripts/git-branches.sh +0 -0
  182. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/scripts/publish.sh +0 -0
  183. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/scripts/release.sh +0 -0
  184. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/MODULE_README.md +0 -0
  185. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/__init__.py +0 -0
  186. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/conftest.py +0 -0
  187. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/MODULE_README.md +0 -0
  188. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/__init__.py +0 -0
  189. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_admin_db_columns.py +0 -0
  190. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_admin_response_rendering.py +0 -0
  191. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_admin_search_negation.py +0 -0
  192. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_admin_search_sentinels.py +0 -0
  193. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_advisory_lock.py +0 -0
  194. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_agent_message.py +0 -0
  195. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_api_auth.py +0 -0
  196. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_api_config.py +0 -0
  197. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_api_handlers.py +0 -0
  198. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_api_protocol.py +0 -0
  199. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_array_agg_order_by.py +0 -0
  200. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_associations.py +0 -0
  201. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_bulk_hydration_offload.py +0 -0
  202. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_bulk_update_by_pk.py +0 -0
  203. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  204. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_cache_debug.py +0 -0
  205. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_call_function.py +0 -0
  206. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_case_when.py +0 -0
  207. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_catalog.py +0 -0
  208. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_composite_pk_filter.py +0 -0
  209. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_composite_pk_write_paths.py +0 -0
  210. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_config.py +0 -0
  211. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_conflict_target.py +0 -0
  212. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_conflict_writes.py +0 -0
  213. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_connection_codecs.py +0 -0
  214. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_connection_poison_guard.py +0 -0
  215. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_connection_primitives.py +0 -0
  216. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_count_composite_distinct.py +0 -0
  217. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_db_function_field.py +0 -0
  218. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_ddl_generator.py +0 -0
  219. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_derived_table.py +0 -0
  220. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_dirty_tracking.py +0 -0
  221. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_dynamic_crud.py +0 -0
  222. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_exceptions.py +0 -0
  223. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  224. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_expression_primitives.py +0 -0
  225. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_fields.py +0 -0
  226. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  227. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_from_alias.py +0 -0
  228. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_fts.py +0 -0
  229. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_func_expression_args.py +0 -0
  230. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_graph_walk.py +0 -0
  231. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_having_annotate_alias.py +0 -0
  232. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  233. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_introspect_rls_policies.py +0 -0
  234. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_join_and_aggregate.py +0 -0
  235. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_latest_rows.py +0 -0
  236. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_listen_notify.py +0 -0
  237. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_local_sqlite.py +0 -0
  238. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_managed_write_guard.py +0 -0
  239. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_matrx_entity.py +0 -0
  240. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_migration_diff_types.py +0 -0
  241. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_migration_loader.py +0 -0
  242. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_model_instance.py +0 -0
  243. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_model_meta.py +0 -0
  244. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_optimistic_concurrency.py +0 -0
  245. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_order_by_expression.py +0 -0
  246. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_parameter_name_hydration.py +0 -0
  247. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_patch_jsonb_path.py +0 -0
  248. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_pool_holder_census.py +0 -0
  249. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_pool_loop_recreation.py +0 -0
  250. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_pool_maintenance.py +0 -0
  251. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_postgrest_filters.py +0 -0
  252. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_pydantic_bridge.py +0 -0
  253. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_query_builder.py +0 -0
  254. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_query_error_detail.py +0 -0
  255. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_query_executor_sql.py +0 -0
  256. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_query_timeout.py +0 -0
  257. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  258. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_queue_claim.py +0 -0
  259. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_read_connection_retry.py +0 -0
  260. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_registry.py +0 -0
  261. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_registry_multi_database.py +0 -0
  262. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_registry_multi_schema.py +0 -0
  263. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_relational_backfill.py +0 -0
  264. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_relations.py +0 -0
  265. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_resilience.py +0 -0
  266. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_rls_session_scope.py +0 -0
  267. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_schema_exists.py +0 -0
  268. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_session_advisory_lock.py +0 -0
  269. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_state_cache.py +0 -0
  270. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_subquery_filter_raw.py +0 -0
  271. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_subquery_in_filter.py +0 -0
  272. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_subquery_nested_expressions.py +0 -0
  273. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_supabase_auth.py +0 -0
  274. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_supabase_config.py +0 -0
  275. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_tls.py +0 -0
  276. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_typed_join_projection.py +0 -0
  277. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_typed_predicates.py +0 -0
  278. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_update_case_expression.py +0 -0
  279. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_update_subquery.py +0 -0
  280. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_update_with_rebase.py +0 -0
  281. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_upsert_default_leak.py +0 -0
  282. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_upsert_with_conflict.py +0 -0
  283. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_values_jsonb_decode.py +0 -0
  284. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level1/test_write_retry_semantics.py +0 -0
  285. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/MODULE_README.md +0 -0
  286. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/__init__.py +0 -0
  287. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/conftest.py +0 -0
  288. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_bulk_ops.py +0 -0
  289. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_cache_integration.py +0 -0
  290. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_crud.py +0 -0
  291. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_foreign_keys.py +0 -0
  292. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_m2m.py +0 -0
  293. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_manager.py +0 -0
  294. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_migrations_live.py +0 -0
  295. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_query_execution.py +0 -0
  296. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/level2/test_schema_diff.py +0 -0
  297. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/live_pooler_role.py +0 -0
  298. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project/.env.example +0 -0
  299. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project/README.md +0 -0
  300. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project/__init__.py +0 -0
  301. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project/generate.py +0 -0
  302. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project/generated/.gitkeep +0 -0
  303. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project/matrx_orm.yaml +0 -0
  304. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project/test_schema_generation.py +0 -0
  305. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project_desktop/.env.example +0 -0
  306. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project_desktop/README.md +0 -0
  307. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project_desktop/__init__.py +0 -0
  308. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project_desktop/client_example.py +0 -0
  309. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project_desktop/client_example.ts +0 -0
  310. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  311. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/sample_project_desktop/server.py +0 -0
  312. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/entity_tests.py +0 -0
  313. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_base_generation.py +0 -0
  314. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  315. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_composite_unique_generation.py +0 -0
  316. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_dto_identity_generation.py +0 -0
  317. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_entity_capability_generation.py +0 -0
  318. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_enum_ordering.py +0 -0
  319. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_generate_schema.py +0 -0
  320. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_junction_analysis.py +0 -0
  321. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_multi_schema_output.py +0 -0
  322. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_schema_analysis.py +0 -0
  323. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_sql_expression_defaults.py +0 -0
  324. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_table_relationships_query.py +0 -0
  325. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema/test_view_generation.py +0 -0
  326. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema_builder/test_array_default_parsing.py +0 -0
  327. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  328. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  329. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/__init__.py +0 -0
  330. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/conftest.py +0 -0
  331. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_cache_no_downgrade.py +0 -0
  332. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_capture_poison_proof.py +0 -0
  333. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_coalesce.py +0 -0
  334. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_dag.py +0 -0
  335. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_disk_spill_fallback.py +0 -0
  336. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_flush_individual_fallback.py +0 -0
  337. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_governed_write.py +0 -0
  338. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_managed.py +0 -0
  339. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_op_enqueue_site.py +0 -0
  340. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_reads.py +0 -0
  341. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/session/test_session.py +0 -0
  342. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_database_name_alias.py +0 -0
  343. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_db_functions.py +0 -0
  344. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_model_cls_refactor.py +0 -0
  345. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  346. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_one_database.py +0 -0
  347. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_pooler_role_guard.py +0 -0
  348. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_secrets_battery.py +0 -0
  349. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_secrets_minting_keys.py +0 -0
  350. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/tests/test_sql_param_casts.py +0 -0
  351. {matrx_orm-3.1.113 → matrx_orm-3.1.115}/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.113
3
+ Version: 3.1.115
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
@@ -34,10 +34,33 @@
34
34
 
35
35
  - Runtime hierarchy and formatting: `exceptions.py`
36
36
  - Failure capture + org stamping: `session/fallback.py`
37
- - Focused unit coverage: `tests/level1/test_exceptions.py`
37
+ - Watched-lifecycle recovery: `session/lifecycle.py`
38
+ - Focused unit coverage: `tests/level1/test_exceptions.py`,
39
+ `tests/level1/test_watched_lifecycle.py`
40
+
41
+ ## Watchdog reads use the resilient SELECT boundary
42
+
43
+ - **Lifecycle SELECTs call `adapter.execute_query`.** They inherit the ORM's
44
+ bounded retry for one transient command timeout on a fresh connection.
45
+ - **Raw borrowed-connection reads are forbidden here.** A direct `conn.fetch`
46
+ bypasses read recovery and turns a healthy table's transient timeout into a
47
+ `lifecycle_watchdog_query_failure`.
48
+
49
+ ## Watchdog transitions are the alert claim
50
+
51
+ - **Only the replica that changes a timed-out row emits its stuck-row alert.**
52
+ `_transition_stuck_row()` returns the guarded UPDATE result; losing replicas
53
+ suppress duplicate `app_log` and `system_error` records for the same row.
54
+ - **Alert-only watches still report the SELECT result.** With no timeout
55
+ transition there is no database claim, so their in-process reminder cadence
56
+ remains authoritative.
38
57
 
39
58
  ## Change Log
40
59
 
60
+ - 2026-08-26 — Made the guarded timeout transition the multi-replica alert
61
+ claim so one stuck row produces one structured watchdog occurrence.
62
+ - 2026-08-26 — Routed watched-lifecycle SELECTs through the resilient adapter
63
+ read boundary so one transient command timeout retries on a fresh connection.
41
64
  - 2026-08-24 — Lifecycle watchdog ERROR boundaries now call `record_error` so
42
65
  stuck rows plus SELECT/transition failures reach `ops.system_error` instead
43
66
  of existing only in `app_log`.
@@ -415,15 +415,18 @@ async def _fetch_stuck_rows(
415
415
 
416
416
  adapter = AdapterRegistry.get(database)
417
417
  sql, params = _build_select_query(cfg, limit)
418
- async with adapter.get_connection() as conn:
419
- return await conn.fetch(sql, *params)
418
+ # Route watchdog reads through the adapter's normal SELECT boundary. That
419
+ # path retries one transient command timeout on a fresh connection; using
420
+ # a raw borrowed connection here bypassed that recovery and emitted a
421
+ # lifecycle_watchdog_query_failure for an otherwise healthy table.
422
+ return await adapter.execute_query(sql, *params)
420
423
 
421
424
 
422
425
  async def _transition_stuck_row(
423
426
  cfg: WatchedLifecycleConfig,
424
427
  pk_value: Any,
425
428
  database: str | None,
426
- ) -> None:
429
+ ) -> bool:
427
430
  """UPDATE one stuck row at timeout. Fully parameterized.
428
431
 
429
432
  Builds the SET list dynamically so a single UPDATE atomically applies, in
@@ -442,7 +445,7 @@ async def _transition_stuck_row(
442
445
  and not cfg.extra_fields_on_timeout
443
446
  and not cfg.extra_expression_fields_on_timeout
444
447
  ):
445
- return
448
+ return False
446
449
 
447
450
  from matrx_orm.adapters import AdapterRegistry
448
451
  from matrx_orm.core.config import get_all_database_project_names
@@ -482,7 +485,7 @@ async def _transition_stuck_row(
482
485
  )
483
486
 
484
487
  if not set_clauses:
485
- return
488
+ return False
486
489
 
487
490
  predicate_sql = cfg.predicate.as_sql(params)
488
491
  params.append(pk_value)
@@ -500,7 +503,10 @@ async def _transition_stuck_row(
500
503
  f"AND {cfg.age_column} < now() - make_interval(secs => ${age_param})"
501
504
  )
502
505
  async with adapter.get_connection() as conn:
503
- await conn.execute(sql, *params)
506
+ result = await conn.execute(sql, *params)
507
+ # Multiple app replicas run the sweep. The guarded UPDATE is the claim:
508
+ # only the replica that changed the row may report it as newly stuck.
509
+ return result == "UPDATE 1"
504
510
 
505
511
 
506
512
  def _format_watchdog_db_failure(
@@ -625,9 +631,34 @@ async def sweep_once(
625
631
  _should_emit_stuck_rows_alert(cfg, stuck_count=0)
626
632
  continue
627
633
 
634
+ claimed_rows = rows
635
+ if cfg.transition_to_on_timeout is not None:
636
+ claimed_rows = []
637
+ for row in rows:
638
+ try:
639
+ if await _transition_stuck_row(cfg, row["pk"], database):
640
+ claimed_rows.append(row)
641
+ except Exception as exc:
642
+ banner, structured = _format_watchdog_db_failure(
643
+ cfg, "UPDATE", exc, pk=row["pk"]
644
+ )
645
+ vcprint(banner, color="red")
646
+ logger.error("watchdog_update_failed", extra=structured)
647
+ await _capture_watchdog_error(
648
+ exc,
649
+ kind="lifecycle_watchdog_transition_failure",
650
+ cfg=cfg,
651
+ phase="update",
652
+ database=database,
653
+ )
654
+
655
+ claimed_count = len(claimed_rows)
656
+ if claimed_count == 0:
657
+ continue
658
+
628
659
  color = _alert_color(cfg.alert_channel)
629
660
  ages_seconds = []
630
- for row in rows:
661
+ for row in claimed_rows:
631
662
  try:
632
663
  aged: datetime = row["aged"]
633
664
  if aged.tzinfo is None:
@@ -637,10 +668,10 @@ async def sweep_once(
637
668
  pass
638
669
  max_age_seen = max(ages_seconds) if ages_seconds else 0
639
670
 
640
- if _should_emit_stuck_rows_alert(cfg, stuck_count=stuck_count):
671
+ if _should_emit_stuck_rows_alert(cfg, stuck_count=claimed_count):
641
672
  summary = _format_stuck_rows_summary(
642
673
  cfg,
643
- stuck_count=stuck_count,
674
+ stuck_count=claimed_count,
644
675
  max_age_seen=max_age_seen,
645
676
  )
646
677
  _emit_stuck_rows_summary(summary, color=color)
@@ -650,7 +681,7 @@ async def sweep_once(
650
681
  "matrx_watchdog": {
651
682
  "table": cfg.table,
652
683
  "schema": cfg.schema,
653
- "stuck_count": stuck_count,
684
+ "stuck_count": claimed_count,
654
685
  "predicate": repr(cfg.predicate),
655
686
  "max_age_secs": cfg.max_age_secs,
656
687
  "alert_channel": cfg.alert_channel,
@@ -664,25 +695,10 @@ async def sweep_once(
664
695
  kind="lifecycle_watchdog_stuck_rows",
665
696
  cfg=cfg,
666
697
  phase="stuck_rows",
667
- stuck_count=stuck_count,
698
+ stuck_count=claimed_count,
668
699
  database=database,
669
700
  )
670
701
 
671
- for row in rows:
672
- try:
673
- await _transition_stuck_row(cfg, row["pk"], database)
674
- except Exception as exc:
675
- banner, structured = _format_watchdog_db_failure(cfg, "UPDATE", exc, pk=row["pk"])
676
- vcprint(banner, color="red")
677
- logger.error("watchdog_update_failed", extra=structured)
678
- await _capture_watchdog_error(
679
- exc,
680
- kind="lifecycle_watchdog_transition_failure",
681
- cfg=cfg,
682
- phase="update",
683
- database=database,
684
- )
685
-
686
702
  return out
687
703
 
688
704
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "matrx-orm"
3
- version = "3.1.113"
3
+ version = "3.1.115"
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" }
@@ -80,6 +80,7 @@ def captured_update(monkeypatch):
80
80
  async def execute(self, sql, *params):
81
81
  captured["sql"] = sql
82
82
  captured["params"] = params
83
+ return "UPDATE 1"
83
84
 
84
85
  class _Adapter:
85
86
  @asynccontextmanager
@@ -138,6 +139,37 @@ async def test_timeout_update_stamps_extra_fields(captured_update):
138
139
  )
139
140
 
140
141
 
142
+ @pytest.mark.asyncio
143
+ async def test_stuck_row_select_uses_resilient_adapter_read(monkeypatch):
144
+ captured: dict[str, object] = {}
145
+
146
+ class _Adapter:
147
+ async def execute_query(self, sql, *params):
148
+ captured["sql"] = sql
149
+ captured["params"] = params
150
+ return [{"pk": "call-1", "aged": lc.datetime.now(lc.UTC)}]
151
+
152
+ def get_connection(self):
153
+ raise AssertionError("watchdog SELECT bypassed the resilient read boundary")
154
+
155
+ fake_adapters = types.ModuleType("matrx_orm.adapters")
156
+ fake_adapters.AdapterRegistry = types.SimpleNamespace(get=lambda db: _Adapter())
157
+ monkeypatch.setitem(sys.modules, "matrx_orm.adapters", fake_adapters)
158
+
159
+ cfg = lc.WatchedLifecycleConfig(
160
+ schema="chat",
161
+ table="tool_call",
162
+ predicate=ArrayAny(F("status"), ["pending", "running"]),
163
+ max_age_secs=1800,
164
+ )
165
+
166
+ rows = await lc._fetch_stuck_rows(cfg, "matrx", 500)
167
+
168
+ assert rows[0]["pk"] == "call-1"
169
+ assert "FROM chat.tool_call" in str(captured["sql"])
170
+ assert captured["params"] == (["pending", "running"], 1800)
171
+
172
+
141
173
  @pytest.mark.asyncio
142
174
  async def test_extra_fields_only_without_status_transition(captured_update):
143
175
  cfg = lc.WatchedLifecycleConfig(
@@ -300,7 +332,7 @@ async def test_sweep_forces_stuck_row_into_system_error(monkeypatch):
300
332
  return [{"pk": "job-1", "aged": lc.datetime.now(lc.UTC)}]
301
333
 
302
334
  async def fake_transition(*_args, **_kwargs):
303
- return None
335
+ return True
304
336
 
305
337
  async def fake_record_error(_exc: BaseException, **fields):
306
338
  captured.append(fields)
@@ -316,6 +348,37 @@ async def test_sweep_forces_stuck_row_into_system_error(monkeypatch):
316
348
  assert [row["kind"] for row in captured] == ["lifecycle_watchdog_stuck_rows"]
317
349
 
318
350
 
351
+ @pytest.mark.asyncio
352
+ async def test_sweep_only_alerts_replica_that_wins_timeout_transition(monkeypatch):
353
+ captured: list[dict] = []
354
+ cfg = lc.WatchedLifecycleConfig(
355
+ table="tool_call",
356
+ schema="chat",
357
+ predicate=Compare(F("status"), "=", "running"),
358
+ max_age_secs=1800,
359
+ transition_to_on_timeout="error",
360
+ )
361
+
362
+ async def fake_fetch(*_args, **_kwargs):
363
+ return [{"pk": "call-1", "aged": lc.datetime.now(lc.UTC)}]
364
+
365
+ async def lost_transition_race(*_args, **_kwargs):
366
+ return False
367
+
368
+ async def fake_record_error(_exc: BaseException, **fields):
369
+ captured.append(fields)
370
+
371
+ monkeypatch.setattr(lc.WatchedLifecycleRegistry, "all", lambda: [cfg])
372
+ monkeypatch.setattr(lc, "_fetch_stuck_rows", fake_fetch)
373
+ monkeypatch.setattr(lc, "_transition_stuck_row", lost_transition_race)
374
+ monkeypatch.setattr(lc, "_emit_stuck_rows_summary", lambda *_args, **_kwargs: None)
375
+ monkeypatch.setattr(lc, "record_error", fake_record_error)
376
+
377
+ await lc.sweep_once(database="matrx")
378
+
379
+ assert captured == []
380
+
381
+
319
382
  def test_alert_only_watch_alerts_on_change_and_hourly_reminder():
320
383
  cfg = lc.WatchedLifecycleConfig(
321
384
  table="system_write_failure",
File without changes
File without changes
File without changes
File without changes
File without changes