matrx-orm 3.1.166__tar.gz → 3.1.168__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 (391) hide show
  1. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/PKG-INFO +2 -1
  2. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/create.py +18 -8
  3. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/FEATURE.md +31 -1
  4. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/attachments.py +212 -158
  5. matrx_orm-3.1.168/matrx_orm/secrets_battery/native_source.py +186 -0
  6. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/pyproject.toml +2 -1
  7. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_query_timeout.py +49 -0
  8. matrx_orm-3.1.168/tests/native_source_cases.py +143 -0
  9. matrx_orm-3.1.168/tests/native_source_oracle.rs +13 -0
  10. matrx_orm-3.1.168/tests/test_attachment_mutation_serialization.py +208 -0
  11. matrx_orm-3.1.168/tests/test_native_source.py +38 -0
  12. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/.gitignore +0 -0
  13. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/AGENTS.md +0 -0
  14. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/BASE_CLASS_METHODS.md +0 -0
  15. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/CLAUDE.md +0 -0
  16. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/MODEL_API.md +0 -0
  17. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/README.md +0 -0
  18. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/database/orm/extended/managers/ai_model_base.py +0 -0
  19. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/docs/migrations.md +0 -0
  20. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/FEATURE.md +0 -0
  21. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/MODULE_README.md +0 -0
  22. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/__init__.py +0 -0
  23. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/MODULE_README.md +0 -0
  24. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/__init__.py +0 -0
  25. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/async_postgresql.py +0 -0
  26. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/base_adapter.py +0 -0
  27. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  28. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/adapters/supabase_adapter.py +0 -0
  29. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/admin/FEATURE.md +0 -0
  30. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/admin/__init__.py +0 -0
  31. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/admin/router.py +0 -0
  32. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/__init__.py +0 -0
  33. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/auth.py +0 -0
  34. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/config.py +0 -0
  35. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/handlers.py +0 -0
  36. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/protocol.py +0 -0
  37. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/api/server.py +0 -0
  38. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/associations.py +0 -0
  39. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/cache_debug.py +0 -0
  40. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/catalog.py +0 -0
  41. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/catalog_sql.py +0 -0
  42. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/MODULE_README.md +0 -0
  43. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/__init__.py +0 -0
  44. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/postgres_connection.py +0 -0
  45. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/postgrest.py +0 -0
  46. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/supabase_auth.py +0 -0
  47. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/supabase_config.py +0 -0
  48. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/client/supabase_manager.py +0 -0
  49. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  50. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/FEATURE.md +0 -0
  51. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/MODULE_README.md +0 -0
  52. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  53. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/TASKS.md +0 -0
  54. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/__init__.py +0 -0
  55. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/async_db_manager.py +0 -0
  56. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/base.py +0 -0
  57. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/config.py +0 -0
  58. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/connections.py +0 -0
  59. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/db_function.py +0 -0
  60. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/diagnostics.py +0 -0
  61. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/expressions.py +0 -0
  62. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/extended.py +0 -0
  63. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/fields.py +0 -0
  64. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/introspection.py +0 -0
  65. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/listen.py +0 -0
  66. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/loop_filters.py +0 -0
  67. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/model_dto.py +0 -0
  68. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/model_view.py +0 -0
  69. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/one_database.py +0 -0
  70. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/paginator.py +0 -0
  71. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/pool_maintenance.py +0 -0
  72. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/pool_watch.py +0 -0
  73. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/pooler_role.py +0 -0
  74. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/pydantic_bridge.py +0 -0
  75. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/registry.py +0 -0
  76. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/relations.py +0 -0
  77. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/resilience.py +0 -0
  78. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/rls_session.py +0 -0
  79. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/session_context.py +0 -0
  80. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/signals.py +0 -0
  81. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/tls.py +0 -0
  82. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/transaction.py +0 -0
  83. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/types.py +0 -0
  84. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/write_queue.py +0 -0
  85. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/core/write_retry.py +0 -0
  86. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/entity.py +0 -0
  87. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/error_handling.py +0 -0
  88. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/exceptions.py +0 -0
  89. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/extended/__init__.py +0 -0
  90. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/extended/app_error_handler.py +0 -0
  91. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/identity_rule.py +0 -0
  92. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/local_sqlite.py +0 -0
  93. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/middleware/__init__.py +0 -0
  94. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/middleware/base.py +0 -0
  95. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/MODULE_README.md +0 -0
  96. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/__init__.py +0 -0
  97. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/cli.py +0 -0
  98. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/ddl.py +0 -0
  99. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/diff.py +0 -0
  100. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/executor.py +0 -0
  101. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/integration.py +0 -0
  102. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/loader.py +0 -0
  103. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/operations.py +0 -0
  104. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/state.py +0 -0
  105. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/migrations/table_filter.py +0 -0
  106. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/no_org_assignment.py +0 -0
  107. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/FEATURE.md +0 -0
  108. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/MODULE_README.md +0 -0
  109. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/__init__.py +0 -0
  110. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/bulk_update_values.py +0 -0
  111. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/catalog_select.py +0 -0
  112. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/conflict.py +0 -0
  113. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/conflict_writes.py +0 -0
  114. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/db_functions.py +0 -0
  115. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/delete.py +0 -0
  116. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/dynamic_admin.py +0 -0
  117. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/dynamic_crud.py +0 -0
  118. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/graph_walk.py +0 -0
  119. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/latest_rows.py +0 -0
  120. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/queue_claim.py +0 -0
  121. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/read.py +0 -0
  122. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/relational_backfill.py +0 -0
  123. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/staging_load.py +0 -0
  124. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/operations/update.py +0 -0
  125. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/platform_access.py +0 -0
  126. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/platform_db.py +0 -0
  127. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/py.typed +0 -0
  128. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/pytest_lock_guard.py +0 -0
  129. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/pytest_plugin.py +0 -0
  130. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/MODULE_README.md +0 -0
  131. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/__init__.py +0 -0
  132. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/db_objects.py +0 -0
  133. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  134. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  135. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/FEATURE.md +0 -0
  136. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/MODULE_README.md +0 -0
  137. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/__init__.py +0 -0
  138. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/builder.py +0 -0
  139. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/query/executor.py +0 -0
  140. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/resources/__init__.py +0 -0
  141. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
  142. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
  143. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/FEATURE.md +0 -0
  144. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  145. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/__init__.py +0 -0
  146. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/code_handler.py +0 -0
  147. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  148. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/columns.py +0 -0
  149. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/common.py +0 -0
  150. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/diff_preview.py +0 -0
  151. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  152. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/generator.py +0 -0
  153. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  154. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  155. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  156. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  157. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/package_wiring.py +0 -0
  158. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/relationships.py +0 -0
  159. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/runner.py +0 -0
  160. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/schema.py +0 -0
  161. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/schema_manager.py +0 -0
  162. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/tables.py +0 -0
  163. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/schema_builder/views.py +0 -0
  164. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/__init__.py +0 -0
  165. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/crypto.py +0 -0
  166. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  167. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/items.py +0 -0
  168. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/model.py +0 -0
  169. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
  170. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/org_service.py +0 -0
  171. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/secrets_battery/service.py +0 -0
  172. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/README.md +0 -0
  173. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/__init__.py +0 -0
  174. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/coalesce.py +0 -0
  175. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/dag.py +0 -0
  176. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/errors.py +0 -0
  177. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/fallback.py +0 -0
  178. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/flush.py +0 -0
  179. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/lifecycle.py +0 -0
  180. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/managed.py +0 -0
  181. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/op.py +0 -0
  182. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/reads.py +0 -0
  183. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/session.py +0 -0
  184. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/session/telemetry.py +0 -0
  185. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  186. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/__init__.py +0 -0
  187. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/executor.py +0 -0
  188. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/queries.py +0 -0
  189. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/registry.py +0 -0
  190. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/types.py +0 -0
  191. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/sql_executor/utils.py +0 -0
  192. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/state.py +0 -0
  193. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/utils/__init__.py +0 -0
  194. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/utils/sql_utils.py +0 -0
  195. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/utils/type_converters.py +0 -0
  196. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/matrx_orm/write_actor.py +0 -0
  197. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/scripts/git-branches.sh +0 -0
  198. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/scripts/publish.sh +0 -0
  199. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/scripts/release.sh +0 -0
  200. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/MODULE_README.md +0 -0
  201. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/__init__.py +0 -0
  202. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/conftest.py +0 -0
  203. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/MODULE_README.md +0 -0
  204. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/__init__.py +0 -0
  205. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_adapter_registry.py +0 -0
  206. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_admin_db_columns.py +0 -0
  207. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_admin_response_rendering.py +0 -0
  208. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_admin_search_negation.py +0 -0
  209. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_admin_search_sentinels.py +0 -0
  210. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_advisory_lock.py +0 -0
  211. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_agent_message.py +0 -0
  212. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_api_auth.py +0 -0
  213. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_api_config.py +0 -0
  214. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_api_handlers.py +0 -0
  215. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_api_protocol.py +0 -0
  216. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_array_agg_order_by.py +0 -0
  217. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_associations.py +0 -0
  218. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_associations_wiring.py +0 -0
  219. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_bulk_hydration_offload.py +0 -0
  220. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_bulk_update_by_pk.py +0 -0
  221. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  222. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_cache_debug.py +0 -0
  223. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_call_function.py +0 -0
  224. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_case_when.py +0 -0
  225. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_catalog.py +0 -0
  226. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_committed_row_guard.py +0 -0
  227. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_composite_pk_filter.py +0 -0
  228. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_composite_pk_write_paths.py +0 -0
  229. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_config.py +0 -0
  230. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_conflict_target.py +0 -0
  231. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_conflict_writes.py +0 -0
  232. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_connection_codecs.py +0 -0
  233. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_connection_poison_guard.py +0 -0
  234. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_connection_primitives.py +0 -0
  235. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_count_composite_distinct.py +0 -0
  236. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_count_limit_param_leak.py +0 -0
  237. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_db_function_field.py +0 -0
  238. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_ddl_generator.py +0 -0
  239. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_deadlock_retry.py +0 -0
  240. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_derived_table.py +0 -0
  241. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_dirty_tracking.py +0 -0
  242. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_dynamic_crud.py +0 -0
  243. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_exceptions.py +0 -0
  244. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  245. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_expression_primitives.py +0 -0
  246. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_fields.py +0 -0
  247. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  248. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_from_alias.py +0 -0
  249. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_fts.py +0 -0
  250. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_func_expression_args.py +0 -0
  251. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_graph_walk.py +0 -0
  252. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_having_annotate_alias.py +0 -0
  253. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  254. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_introspect_rls_policies.py +0 -0
  255. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_join_and_aggregate.py +0 -0
  256. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_latest_rows.py +0 -0
  257. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_listen_notify.py +0 -0
  258. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_local_sqlite.py +0 -0
  259. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_lock_contention_retry.py +0 -0
  260. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_managed_write_guard.py +0 -0
  261. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_matrx_entity.py +0 -0
  262. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_migration_diff_types.py +0 -0
  263. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_migration_loader.py +0 -0
  264. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_model_instance.py +0 -0
  265. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_model_meta.py +0 -0
  266. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_optimistic_concurrency.py +0 -0
  267. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_order_by_expression.py +0 -0
  268. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_org_assignment_inventory.py +0 -0
  269. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_orm_banners_are_client_safe.py +0 -0
  270. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_parameter_name_hydration.py +0 -0
  271. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_patch_jsonb_path.py +0 -0
  272. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_persisted_count.py +0 -0
  273. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_pool_holder_census.py +0 -0
  274. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_pool_loop_recreation.py +0 -0
  275. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_pool_maintenance.py +0 -0
  276. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_postgrest_filters.py +0 -0
  277. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_pydantic_bridge.py +0 -0
  278. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_query_builder.py +0 -0
  279. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_query_error_detail.py +0 -0
  280. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_query_executor_sql.py +0 -0
  281. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  282. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_queue_claim.py +0 -0
  283. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_read_connection_retry.py +0 -0
  284. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_registry.py +0 -0
  285. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_registry_multi_database.py +0 -0
  286. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_registry_multi_schema.py +0 -0
  287. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_relational_backfill.py +0 -0
  288. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_relations.py +0 -0
  289. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_resilience.py +0 -0
  290. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_rls_session_scope.py +0 -0
  291. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_schema_exists.py +0 -0
  292. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_session_advisory_lock.py +0 -0
  293. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_standalone_write_session_context.py +0 -0
  294. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_state_cache.py +0 -0
  295. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_subquery_filter_raw.py +0 -0
  296. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_subquery_in_filter.py +0 -0
  297. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_subquery_nested_expressions.py +0 -0
  298. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_supabase_auth.py +0 -0
  299. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_supabase_config.py +0 -0
  300. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_tls.py +0 -0
  301. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_transaction_database_identity.py +0 -0
  302. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_typed_join_projection.py +0 -0
  303. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_typed_predicates.py +0 -0
  304. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_update_case_expression.py +0 -0
  305. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_update_subquery.py +0 -0
  306. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_update_with_rebase.py +0 -0
  307. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_upsert_default_leak.py +0 -0
  308. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_upsert_with_conflict.py +0 -0
  309. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_values_jsonb_decode.py +0 -0
  310. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_watched_lifecycle.py +0 -0
  311. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_write_queue_deadline.py +0 -0
  312. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level1/test_write_retry_semantics.py +0 -0
  313. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/MODULE_README.md +0 -0
  314. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/__init__.py +0 -0
  315. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/conftest.py +0 -0
  316. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_bulk_ops.py +0 -0
  317. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_cache_integration.py +0 -0
  318. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_crud.py +0 -0
  319. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_foreign_keys.py +0 -0
  320. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_m2m.py +0 -0
  321. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_manager.py +0 -0
  322. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_migrations_live.py +0 -0
  323. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_query_execution.py +0 -0
  324. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/test_schema_diff.py +0 -0
  325. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/level2/verify_committed_row_guard_pg17.py +0 -0
  326. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/live_pooler_role.py +0 -0
  327. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/.env.example +0 -0
  328. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/README.md +0 -0
  329. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/__init__.py +0 -0
  330. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/generate.py +0 -0
  331. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/generated/.gitkeep +0 -0
  332. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/matrx_orm.yaml +0 -0
  333. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project/test_schema_generation.py +0 -0
  334. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/.env.example +0 -0
  335. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/README.md +0 -0
  336. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/__init__.py +0 -0
  337. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/client_example.py +0 -0
  338. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/client_example.ts +0 -0
  339. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  340. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/sample_project_desktop/server.py +0 -0
  341. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/entity_tests.py +0 -0
  342. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_base_generation.py +0 -0
  343. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  344. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_composite_unique_generation.py +0 -0
  345. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_dto_identity_generation.py +0 -0
  346. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_entity_capability_generation.py +0 -0
  347. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_enum_ordering.py +0 -0
  348. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_generate_schema.py +0 -0
  349. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_junction_analysis.py +0 -0
  350. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_multi_schema_output.py +0 -0
  351. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_schema_analysis.py +0 -0
  352. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_sql_expression_defaults.py +0 -0
  353. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_table_relationships_query.py +0 -0
  354. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema/test_view_generation.py +0 -0
  355. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema_builder/test_array_default_parsing.py +0 -0
  356. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  357. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  358. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/__init__.py +0 -0
  359. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/conftest.py +0 -0
  360. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_cache_no_downgrade.py +0 -0
  361. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_capture_poison_proof.py +0 -0
  362. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_coalesce.py +0 -0
  363. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_dag.py +0 -0
  364. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_disk_spill_fallback.py +0 -0
  365. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_flush_individual_fallback.py +0 -0
  366. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_governed_write.py +0 -0
  367. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_managed.py +0 -0
  368. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_op_enqueue_site.py +0 -0
  369. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_reads.py +0 -0
  370. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_record_error_bounded_insert.py +0 -0
  371. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_record_error_storm.py +0 -0
  372. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_session.py +0 -0
  373. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/session/test_spill_guard_plugin.py +0 -0
  374. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_array_arg_element_type.py +0 -0
  375. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_database_name_alias.py +0 -0
  376. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_db_functions.py +0 -0
  377. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_dynamic_select_unregistered.py +0 -0
  378. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_immutable_insert_or_exact_verify.py +0 -0
  379. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_keyed_digest.py +0 -0
  380. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_model_cls_refactor.py +0 -0
  381. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  382. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_one_database.py +0 -0
  383. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_order_by_desc_nulls_last.py +0 -0
  384. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_pooler_role_guard.py +0 -0
  385. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_secret_audit_names_its_organization.py +0 -0
  386. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_secrets_battery.py +0 -0
  387. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_secrets_minting_keys.py +0 -0
  388. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_shared_lock_guard.py +0 -0
  389. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_sql_param_casts.py +0 -0
  390. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_sync_twins_guard.py +0 -0
  391. {matrx_orm-3.1.166 → matrx_orm-3.1.168}/tests/test_user_visible_lookup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.166
3
+ Version: 3.1.168
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
@@ -23,6 +23,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Classifier: Typing :: Typed
24
24
  Requires-Python: >=3.13
25
25
  Requires-Dist: asyncpg>=0.31.0
26
+ Requires-Dist: cbor2<7,>=6.1.3
26
27
  Requires-Dist: cryptography>=43.0
27
28
  Requires-Dist: gitpython>=3.1.40
28
29
  Requires-Dist: matrx-utils>=2.0.50
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  from typing import Any, TYPE_CHECKING, Union
4
5
 
5
6
  from matrx_orm.state import StateManager
@@ -20,6 +21,19 @@ if TYPE_CHECKING:
20
21
  OnConflictArg = Union[list[str], "ConflictTarget"]
21
22
 
22
23
 
24
+ def _prepare_instance_data(model_cls: type[Model], instance: Model) -> dict[str, Any]:
25
+ """Coerce a single insert row without occupying the request event loop."""
26
+ data: dict[str, Any] = {}
27
+ for field_name, field in model_cls._fields.items():
28
+ if isinstance(field, Field):
29
+ value = getattr(instance, field_name)
30
+ if value is None and field.default is not None:
31
+ value = field.get_default()
32
+ if value is not None:
33
+ data[field_name] = field.get_db_prep_value(value)
34
+ return data
35
+
36
+
23
37
  def _normalise_on_conflict(on_conflict: OnConflictArg) -> tuple[list[str], ConflictTarget | None]:
24
38
  """Internal: unify the two acceptable shapes.
25
39
 
@@ -85,14 +99,10 @@ async def save(instance: Model, *, statement_timeout: float | None = None) -> Mo
85
99
  model_cls = instance.__class__
86
100
  await pre_create.send(model_cls, instance=instance)
87
101
 
88
- data: dict[str, Any] = {}
89
- for field_name, field in model_cls._fields.items():
90
- if isinstance(field, Field):
91
- value = getattr(instance, field_name)
92
- if value is None and field.default is not None:
93
- value = field.get_default()
94
- if value is not None:
95
- data[field_name] = field.get_db_prep_value(value)
102
+ # JSONB conversion can recursively walk a large OCR/PDF payload and take
103
+ # seconds. This is a shared write boundary, so every single-row create
104
+ # gets the same event-loop protection as the existing bulk write paths.
105
+ data = await asyncio.to_thread(_prepare_instance_data, model_cls, instance)
96
106
 
97
107
  query = QueryBuilder(model_cls)._build_query()
98
108
  query["data"] = data
@@ -57,6 +57,8 @@ fork, and transfer refuse a protected child. Every competing child/lifecycle
57
57
  mutation locks and re-reads the parent in its transaction; whole-item delete
58
58
  remains atomic.
59
59
 
60
+ **Native source admission (`native_source.py`).** `validate_native_source` is the server reader of the provider's exact source-v1 byte format; Rust remains the encoder and authenticator. It requires an explicit positive source-size limit, preserves nullable names and bytes, rejects duplicate/noncanonical JSON and COSE, and derives public metadata only after maintained P-256 key-pair validation. The result is provider-custody data, never a response model or generic field. `repr` and fixed failures omit material. `scripts/check_native_source_parity.py --rust-core <native-vault-provider/core>` compares the real compiled Rust reader against the server corpus; no mocked oracle. This prerequisite adds no creator, materializer, database row or native readiness. Verified September19 against132 focused tests and525 cross-language cases.
61
+
60
62
  **`inject_into_sandbox=true` REQUIRES an env alias — refused otherwise.** A
61
63
  container environment is a NAME→value map, so a field with `key IS NULL` has
62
64
  nowhere to land and bulk resolution drops it. Accepting the flag anyway meant a
@@ -94,7 +96,33 @@ refuses sealed bytes; `resolve_credential_attachment` is the trusted in-process
94
96
  path. Parent delete soft-deletes attachments atomically; fork decrypts and
95
97
  re-encrypts independent child rows; transfer needs no child rewrite because
96
98
  the parent is the sole ownership authority. Browser-facing list shapes never
97
- contain ciphertext or plaintext.
99
+ contain ciphertext or plaintext. List serialization is deliberately outside
100
+ the mutation and byte-materialization locking boundary.
101
+
102
+ **Attachment serialization.** Every attachment mutation and byte materialization
103
+ first identifies its parent, then in one `transaction(PACKAGE_DB_NAME)` locks and
104
+ re-reads the live parent before it locks and re-reads the live child. The write
105
+ or byte materialization and any audit use that same transaction. Propagated
106
+ admission or operation errors roll the mutation back; database audit-storage
107
+ errors retain `record_secret_audit`'s existing best-effort savepoint behavior.
108
+ This is the
109
+ same parent-first order as item delete, so delete wins cleanly over an add,
110
+ metadata patch, replacement, child delete, human download, or trusted resolve;
111
+ no child can be created, changed, or materialized under a deleted parent. The
112
+ parent-delete helper deliberately takes no second parent lock because its caller
113
+ already owns it; fork still locks its source parent and copies only its stable
114
+ snapshot.
115
+
116
+ **Live race recipe (requires an explicitly authorized disposable admin item).**
117
+ Through the real Vault router and auth provider, create an admin-owned item and
118
+ pause the attachment request after it takes its parent lock but before its child
119
+ write. Releasing it first must return 201; the following item delete returns
120
+ 204 and a fresh router list has no live child. In the inverse ordering, complete
121
+ the delete first and then release the attachment request: it must be refused
122
+ and must not leave a live child. Repeat with download and trusted resolve paused
123
+ before byte materialization. Remove the disposable item through the canonical
124
+ cascade after each run. This is a live concurrency probe, never a replacement
125
+ for the focused unit guards.
98
126
 
99
127
  **Grants: use the single-grant operations.** `add_item_grant` /
100
128
  `update_item_grant` / `remove_item_grant` change ONE recipient and leave the
@@ -328,6 +356,8 @@ delete).
328
356
 
329
357
  ## Change Log
330
358
 
359
+ - **2026-09-19** — Added bounded source-v1 server admission reader with cbor2/cryptography and actual Rust differential parity; protected storage and native ceremonies remain separate pending units.
360
+
331
361
  - **2026-09-17 (3.1.160)** — **An audit row NAMES its organization, or nothing is
332
362
  written.** `record_secret_audit(organization_id=...)` is REQUIRED and refuses an
333
363
  empty value by name; it used to default to `None` "for legacy callers", and 51 of
@@ -12,9 +12,11 @@ from dataclasses import dataclass
12
12
  from datetime import UTC, datetime
13
13
  from typing import Any
14
14
 
15
+ from matrx_orm import transaction
16
+
15
17
  from .crypto import decrypt_bytes, encrypt_bytes
16
- from .items import CredentialItemNotFoundError, HANDLING_MODES, SealedFieldError
17
- from .model import CredentialAttachment, CredentialItem
18
+ from .items import CredentialItemNotFoundError, HANDLING_MODES, SealedFieldError, _lock_item_raw
19
+ from .model import PACKAGE_DB_NAME, CredentialAttachment, CredentialItem
18
20
  from .org_service import audit_organization, record_secret_audit
19
21
  from .service import SecretDecryptError
20
22
 
@@ -111,6 +113,20 @@ async def _require_raw(attachment_id: str) -> dict[str, Any]:
111
113
  return rows[0]
112
114
 
113
115
 
116
+ async def _lock_attachment_raw(
117
+ attachment_id: str, *, parent_item_id: str
118
+ ) -> dict[str, Any]:
119
+ """Lock and re-read a live child after its parent is locked."""
120
+ rows = await (
121
+ CredentialAttachment.filter(id=attachment_id, deleted_at__isnull=True)
122
+ .select_for_update()
123
+ .values(*_ATTACHMENT_FIELDS, "value_encrypted")
124
+ )
125
+ if not rows or str(rows[0]["credential_item_id"]) != parent_item_id:
126
+ raise LookupError(f"credential attachment {attachment_id} not found")
127
+ return rows[0]
128
+
129
+
114
130
  def _validate_text(value: str, *, name: str, maximum: int) -> str:
115
131
  if not isinstance(value, str) or not value.strip():
116
132
  raise ValueError(f"{name} is required")
@@ -177,8 +193,6 @@ async def add_credential_attachment(
177
193
  audit_organization_id: str | None = None,
178
194
  ) -> CredentialAttachmentRow:
179
195
  """Encrypt and attach one file to an existing credential item."""
180
- await _require_item(item_id)
181
- parent_org = await _parent_item_organization(item_id)
182
196
  _validate_content(content)
183
197
  _validate_handling(handling)
184
198
  clean_label = _validate_text(label, name="label", maximum=200)
@@ -188,37 +202,44 @@ async def add_credential_attachment(
188
202
  if description and description.strip()
189
203
  else None
190
204
  )
191
- instance = await CredentialAttachment.create(
192
- credential_item_id=item_id,
193
- label=clean_label,
194
- description=clean_description,
195
- file_name=clean_name,
196
- media_type=_media_type(media_type),
197
- size_bytes=len(content),
198
- value_encrypted=encrypt_bytes(content),
199
- handling=handling,
200
- created_by=actor_id,
201
- updated_by=actor_id,
202
- )
203
- await record_secret_audit(
204
- action="attachment_added",
205
- actor_id=actor_id,
206
- organization_id=audit_organization(
207
- record_organization_id=parent_org,
208
- request_organization_id=audit_organization_id,
209
- what=f"add_credential_attachment(item_id={item_id})",
210
- ),
211
- metadata={
212
- "credential_item_id": item_id,
213
- "attachment_id": str(instance.id),
214
- "file_name": clean_name,
215
- "size_bytes": len(content),
216
- "handling": handling,
217
- },
218
- )
219
- row = await get_credential_attachment(str(instance.id))
205
+ encrypted_content = encrypt_bytes(content)
206
+ instance_id: str | None = None
207
+ async with transaction(PACKAGE_DB_NAME):
208
+ parent = await _lock_item_raw(item_id)
209
+ instance = await CredentialAttachment.create(
210
+ credential_item_id=item_id,
211
+ label=clean_label,
212
+ description=clean_description,
213
+ file_name=clean_name,
214
+ media_type=_media_type(media_type),
215
+ size_bytes=len(content),
216
+ value_encrypted=encrypted_content,
217
+ handling=handling,
218
+ created_by=actor_id,
219
+ updated_by=actor_id,
220
+ )
221
+ instance_id = str(instance.id)
222
+ await record_secret_audit(
223
+ action="attachment_added",
224
+ actor_id=actor_id,
225
+ organization_id=audit_organization(
226
+ record_organization_id=parent.get("organization_id"),
227
+ request_organization_id=audit_organization_id,
228
+ what=f"add_credential_attachment(item_id={item_id})",
229
+ ),
230
+ metadata={
231
+ "credential_item_id": item_id,
232
+ "attachment_id": instance_id,
233
+ "file_name": clean_name,
234
+ "size_bytes": len(content),
235
+ "handling": handling,
236
+ },
237
+ )
238
+ if instance_id is None:
239
+ raise RuntimeError("credential attachment create completed without an id")
240
+ row = await get_credential_attachment(instance_id)
220
241
  if row is None:
221
- raise RuntimeError(f"credential attachment {instance.id} vanished after create")
242
+ raise RuntimeError(f"credential attachment {instance_id} vanished after create")
222
243
  return row
223
244
 
224
245
 
@@ -236,14 +257,8 @@ async def update_credential_attachment_metadata(
236
257
  audit_organization_id: str | None = None,
237
258
  ) -> CredentialAttachmentRow:
238
259
  """Patch safe metadata without touching bytes or ``value_version``."""
239
- raw = await _require_raw(attachment_id)
240
- parent_org = await _parent_item_organization(str(raw["credential_item_id"]))
241
- if handling is not None:
242
- _validate_handling(handling)
243
- if str(raw["handling"]) == "sealed" and handling != "sealed":
244
- raise SealedFieldError(
245
- f"attachment {raw['file_name']!r} is sealed — sealing is a one-way door"
246
- )
260
+ initial = await _require_raw(attachment_id)
261
+ parent_item_id = str(initial["credential_item_id"])
247
262
  updates: dict[str, Any] = {"updated_by": actor_id, "updated_at": datetime.now(UTC)}
248
263
  if label is not None:
249
264
  updates["label"] = _validate_text(label, name="label", maximum=200)
@@ -260,21 +275,32 @@ async def update_credential_attachment_metadata(
260
275
  changed = sorted(set(updates) - {"updated_by", "updated_at"})
261
276
  if not changed:
262
277
  raise ValueError("no attachment metadata changes supplied")
263
- await CredentialAttachment.update_where({"id": attachment_id}, **updates)
264
- await record_secret_audit(
265
- action="attachment_updated",
266
- actor_id=actor_id,
267
- organization_id=audit_organization(
268
- record_organization_id=parent_org,
269
- request_organization_id=audit_organization_id,
270
- what=f"update_credential_attachment_metadata(attachment_id={attachment_id})",
271
- ),
272
- metadata={
273
- "credential_item_id": str(raw["credential_item_id"]),
274
- "attachment_id": attachment_id,
275
- "changed": changed,
276
- },
277
- )
278
+ if handling is not None:
279
+ _validate_handling(handling)
280
+ async with transaction(PACKAGE_DB_NAME):
281
+ parent = await _lock_item_raw(parent_item_id)
282
+ raw = await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
283
+ if handling is not None and str(raw["handling"]) == "sealed" and handling != "sealed":
284
+ raise SealedFieldError(
285
+ f"attachment {raw['file_name']!r} is sealed — sealing is a one-way door"
286
+ )
287
+ await CredentialAttachment.update_where(
288
+ {"id": attachment_id, "deleted_at__isnull": True}, **updates
289
+ )
290
+ await record_secret_audit(
291
+ action="attachment_updated",
292
+ actor_id=actor_id,
293
+ organization_id=audit_organization(
294
+ record_organization_id=parent.get("organization_id"),
295
+ request_organization_id=audit_organization_id,
296
+ what=f"update_credential_attachment_metadata(attachment_id={attachment_id})",
297
+ ),
298
+ metadata={
299
+ "credential_item_id": parent_item_id,
300
+ "attachment_id": attachment_id,
301
+ "changed": changed,
302
+ },
303
+ )
278
304
  row = await get_credential_attachment(attachment_id)
279
305
  if row is None:
280
306
  raise LookupError(f"credential attachment {attachment_id} not found")
@@ -291,34 +317,38 @@ async def replace_credential_attachment(
291
317
  audit_organization_id: str | None = None,
292
318
  ) -> CredentialAttachmentRow:
293
319
  """Atomically replace the encrypted bytes and file metadata."""
294
- raw = await _require_raw(attachment_id)
295
- parent_org = await _parent_item_organization(str(raw["credential_item_id"]))
320
+ initial = await _require_raw(attachment_id)
321
+ parent_item_id = str(initial["credential_item_id"])
296
322
  _validate_content(content)
297
323
  clean_name = _validate_text(file_name, name="file_name", maximum=512)
298
- await CredentialAttachment.update_where(
299
- {"id": attachment_id, "deleted_at__isnull": True},
300
- file_name=clean_name,
301
- media_type=_media_type(media_type),
302
- size_bytes=len(content),
303
- value_encrypted=encrypt_bytes(content),
304
- updated_by=actor_id,
305
- updated_at=datetime.now(UTC),
306
- )
307
- await record_secret_audit(
308
- action="attachment_replaced",
309
- actor_id=actor_id,
310
- organization_id=audit_organization(
311
- record_organization_id=parent_org,
312
- request_organization_id=audit_organization_id,
313
- what=f"replace_credential_attachment(attachment_id={attachment_id})",
314
- ),
315
- metadata={
316
- "credential_item_id": str(raw["credential_item_id"]),
317
- "attachment_id": attachment_id,
318
- "file_name": clean_name,
319
- "size_bytes": len(content),
320
- },
321
- )
324
+ encrypted_content = encrypt_bytes(content)
325
+ async with transaction(PACKAGE_DB_NAME):
326
+ parent = await _lock_item_raw(parent_item_id)
327
+ await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
328
+ await CredentialAttachment.update_where(
329
+ {"id": attachment_id, "deleted_at__isnull": True},
330
+ file_name=clean_name,
331
+ media_type=_media_type(media_type),
332
+ size_bytes=len(content),
333
+ value_encrypted=encrypted_content,
334
+ updated_by=actor_id,
335
+ updated_at=datetime.now(UTC),
336
+ )
337
+ await record_secret_audit(
338
+ action="attachment_replaced",
339
+ actor_id=actor_id,
340
+ organization_id=audit_organization(
341
+ record_organization_id=parent.get("organization_id"),
342
+ request_organization_id=audit_organization_id,
343
+ what=f"replace_credential_attachment(attachment_id={attachment_id})",
344
+ ),
345
+ metadata={
346
+ "credential_item_id": parent_item_id,
347
+ "attachment_id": attachment_id,
348
+ "file_name": clean_name,
349
+ "size_bytes": len(content),
350
+ },
351
+ )
322
352
  row = await get_credential_attachment(attachment_id)
323
353
  if row is None:
324
354
  raise LookupError(f"credential attachment {attachment_id} not found")
@@ -331,31 +361,36 @@ async def delete_credential_attachment(
331
361
  actor_id: str | None = None,
332
362
  audit_organization_id: str | None = None,
333
363
  ) -> bool:
334
- raw = await _require_raw(attachment_id)
335
- parent_org = await _parent_item_organization(str(raw["credential_item_id"]))
364
+ initial = await _require_raw(attachment_id)
365
+ parent_item_id = str(initial["credential_item_id"])
336
366
  now = datetime.now(UTC)
337
- result = await CredentialAttachment.update_where(
338
- {"id": attachment_id, "deleted_at__isnull": True},
339
- deleted_at=now,
340
- updated_by=actor_id,
341
- updated_at=now,
342
- )
343
- if result.rows_affected:
344
- await record_secret_audit(
345
- action="attachment_deleted",
346
- actor_id=actor_id,
347
- organization_id=audit_organization(
348
- record_organization_id=parent_org,
349
- request_organization_id=audit_organization_id,
350
- what=f"delete_credential_attachment(attachment_id={attachment_id})",
351
- ),
352
- metadata={
353
- "credential_item_id": str(raw["credential_item_id"]),
354
- "attachment_id": attachment_id,
355
- "file_name": str(raw["file_name"]),
356
- },
367
+ deleted = False
368
+ async with transaction(PACKAGE_DB_NAME):
369
+ parent = await _lock_item_raw(parent_item_id)
370
+ raw = await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
371
+ result = await CredentialAttachment.update_where(
372
+ {"id": attachment_id, "deleted_at__isnull": True},
373
+ deleted_at=now,
374
+ updated_by=actor_id,
375
+ updated_at=now,
357
376
  )
358
- return result.rows_affected > 0
377
+ if result.rows_affected:
378
+ deleted = True
379
+ await record_secret_audit(
380
+ action="attachment_deleted",
381
+ actor_id=actor_id,
382
+ organization_id=audit_organization(
383
+ record_organization_id=parent.get("organization_id"),
384
+ request_organization_id=audit_organization_id,
385
+ what=f"delete_credential_attachment(attachment_id={attachment_id})",
386
+ ),
387
+ metadata={
388
+ "credential_item_id": parent_item_id,
389
+ "attachment_id": attachment_id,
390
+ "file_name": str(raw["file_name"]),
391
+ },
392
+ )
393
+ return deleted
359
394
 
360
395
 
361
396
  async def reveal_credential_attachment(
@@ -365,64 +400,83 @@ async def reveal_credential_attachment(
365
400
  audit_organization_id: str | None = None,
366
401
  ) -> tuple[CredentialAttachmentRow, bytes]:
367
402
  """Human download path. Sealed bytes are structurally refused."""
368
- raw = await _require_raw(attachment_id)
369
- parent_org = await _parent_item_organization(str(raw["credential_item_id"]))
370
- if str(raw["handling"]) == "sealed":
371
- await record_secret_audit(
372
- action="attachment_download_denied",
373
- actor_id=actor_id,
374
- organization_id=audit_organization(
375
- record_organization_id=parent_org,
376
- request_organization_id=audit_organization_id,
377
- what=f"reveal_credential_attachment(attachment_id={attachment_id})",
378
- ),
379
- metadata={
380
- "credential_item_id": str(raw["credential_item_id"]),
381
- "attachment_id": attachment_id,
382
- "reason": "sealed",
383
- },
384
- )
385
- raise SealedFieldError(
386
- f"attachment {raw['file_name']!r} is sealed — no human-facing API may return it"
387
- )
388
- try:
389
- content = decrypt_bytes(raw["value_encrypted"])
390
- except Exception as exc:
391
- raise SecretDecryptError(
392
- f"credential attachment {attachment_id} does not decrypt with the configured key"
393
- ) from exc
394
- await CredentialAttachment.update_where({"id": attachment_id}, last_used_at=datetime.now(UTC))
395
- await record_secret_audit(
396
- action="attachment_downloaded",
397
- actor_id=actor_id,
398
- organization_id=audit_organization(
399
- record_organization_id=parent_org,
400
- request_organization_id=audit_organization_id,
401
- what=f"reveal_credential_attachment(attachment_id={attachment_id})",
402
- ),
403
- metadata={
404
- "credential_item_id": str(raw["credential_item_id"]),
405
- "attachment_id": attachment_id,
406
- "file_name": str(raw["file_name"]),
407
- "size_bytes": int(raw["size_bytes"]),
408
- },
409
- )
410
- return _row(raw), content
403
+ initial = await _require_raw(attachment_id)
404
+ parent_item_id = str(initial["credential_item_id"])
405
+ denied_error: SealedFieldError | None = None
406
+ result: tuple[CredentialAttachmentRow, bytes] | None = None
407
+ async with transaction(PACKAGE_DB_NAME):
408
+ parent = await _lock_item_raw(parent_item_id)
409
+ raw = await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
410
+ if str(raw["handling"]) == "sealed":
411
+ await record_secret_audit(
412
+ action="attachment_download_denied",
413
+ actor_id=actor_id,
414
+ organization_id=audit_organization(
415
+ record_organization_id=parent.get("organization_id"),
416
+ request_organization_id=audit_organization_id,
417
+ what=f"reveal_credential_attachment(attachment_id={attachment_id})",
418
+ ),
419
+ metadata={
420
+ "credential_item_id": parent_item_id,
421
+ "attachment_id": attachment_id,
422
+ "reason": "sealed",
423
+ },
424
+ )
425
+ denied_error = SealedFieldError(
426
+ f"attachment {raw['file_name']!r} is sealed — no human-facing API may return it"
427
+ )
428
+ else:
429
+ try:
430
+ content = decrypt_bytes(raw["value_encrypted"])
431
+ except Exception as exc:
432
+ raise SecretDecryptError(
433
+ f"credential attachment {attachment_id} does not decrypt with the configured key"
434
+ ) from exc
435
+ await CredentialAttachment.update_where(
436
+ {"id": attachment_id, "deleted_at__isnull": True}, last_used_at=datetime.now(UTC)
437
+ )
438
+ await record_secret_audit(
439
+ action="attachment_downloaded",
440
+ actor_id=actor_id,
441
+ organization_id=audit_organization(
442
+ record_organization_id=parent.get("organization_id"),
443
+ request_organization_id=audit_organization_id,
444
+ what=f"reveal_credential_attachment(attachment_id={attachment_id})",
445
+ ),
446
+ metadata={
447
+ "credential_item_id": parent_item_id,
448
+ "attachment_id": attachment_id,
449
+ "file_name": str(raw["file_name"]),
450
+ "size_bytes": int(raw["size_bytes"]),
451
+ },
452
+ )
453
+ result = (_row(raw), content)
454
+ if denied_error is not None:
455
+ raise denied_error
456
+ if result is None:
457
+ raise RuntimeError("attachment download completed without a result")
458
+ return result
411
459
 
412
460
 
413
461
  async def resolve_credential_attachment(
414
462
  attachment_id: str,
415
463
  ) -> tuple[CredentialAttachmentRow, bytes]:
416
464
  """Trusted execution path, including sealed attachments."""
417
- raw = await _require_raw(attachment_id)
418
- try:
419
- content = decrypt_bytes(raw["value_encrypted"])
420
- except Exception as exc:
421
- raise SecretDecryptError(
422
- f"credential attachment {attachment_id} does not decrypt with the configured key"
423
- ) from exc
424
- await CredentialAttachment.update_where({"id": attachment_id}, last_used_at=datetime.now(UTC))
425
- return _row(raw), content
465
+ initial = await _require_raw(attachment_id)
466
+ parent_item_id = str(initial["credential_item_id"])
467
+ async with transaction(PACKAGE_DB_NAME):
468
+ await _lock_item_raw(parent_item_id)
469
+ raw = await _lock_attachment_raw(attachment_id, parent_item_id=parent_item_id)
470
+ try:
471
+ content = decrypt_bytes(raw["value_encrypted"])
472
+ except Exception as exc:
473
+ raise SecretDecryptError(
474
+ f"credential attachment {attachment_id} does not decrypt with the configured key"
475
+ ) from exc
476
+ await CredentialAttachment.update_where(
477
+ {"id": attachment_id, "deleted_at__isnull": True}, last_used_at=datetime.now(UTC)
478
+ )
479
+ return _row(raw), content
426
480
 
427
481
 
428
482
  async def soft_delete_item_attachments(item_id: str, *, actor_id: str | None = None) -> int: