matrx-orm 3.1.140__tar.gz → 3.1.142__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 (376) hide show
  1. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/CLAUDE.md +1 -1
  2. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/PKG-INFO +2 -2
  3. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/async_db_manager.py +31 -9
  4. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/FEATURE.md +10 -0
  5. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/__init__.py +2 -0
  6. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/items.py +181 -74
  7. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/model.py +1 -0
  8. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/org_service.py +92 -43
  9. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/service.py +9 -3
  10. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/pyproject.toml +2 -2
  11. matrx_orm-3.1.142/tests/level1/test_standalone_write_session_context.py +139 -0
  12. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_secrets_battery.py +79 -2
  13. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_secrets_minting_keys.py +5 -0
  14. matrx_orm-3.1.140/tests/level1/test_standalone_write_session_context.py +0 -81
  15. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/.gitignore +0 -0
  16. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/AGENTS.md +0 -0
  17. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/BASE_CLASS_METHODS.md +0 -0
  18. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/MODEL_API.md +0 -0
  19. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/README.md +0 -0
  20. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/database/orm/extended/managers/ai_model_base.py +0 -0
  21. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/docs/migrations.md +0 -0
  22. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/FEATURE.md +0 -0
  23. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/MODULE_README.md +0 -0
  24. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/__init__.py +0 -0
  25. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/adapters/MODULE_README.md +0 -0
  26. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/adapters/__init__.py +0 -0
  27. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/adapters/async_postgresql.py +0 -0
  28. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/adapters/base_adapter.py +0 -0
  29. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/adapters/postgresql.py +0 -0
  30. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/adapters/postgrest_client_adapter.py +0 -0
  31. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/adapters/supabase_adapter.py +0 -0
  32. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/admin/FEATURE.md +0 -0
  33. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/admin/__init__.py +0 -0
  34. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/admin/router.py +0 -0
  35. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/api/__init__.py +0 -0
  36. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/api/auth.py +0 -0
  37. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/api/config.py +0 -0
  38. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/api/handlers.py +0 -0
  39. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/api/protocol.py +0 -0
  40. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/api/server.py +0 -0
  41. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/associations.py +0 -0
  42. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/cache_debug.py +0 -0
  43. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/catalog.py +0 -0
  44. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/catalog_sql.py +0 -0
  45. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/client/MODULE_README.md +0 -0
  46. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/client/__init__.py +0 -0
  47. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/client/postgres_connection.py +0 -0
  48. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/client/postgrest.py +0 -0
  49. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/client/supabase_auth.py +0 -0
  50. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/client/supabase_config.py +0 -0
  51. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/client/supabase_manager.py +0 -0
  52. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/EXTENDED-TASK.md +0 -0
  53. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/FEATURE.md +0 -0
  54. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/MODULE_README.md +0 -0
  55. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/RELATIONS-TASKS.md +0 -0
  56. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/TASKS.md +0 -0
  57. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/__init__.py +0 -0
  58. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/base.py +0 -0
  59. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/config.py +0 -0
  60. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/connections.py +0 -0
  61. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/db_function.py +0 -0
  62. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/diagnostics.py +0 -0
  63. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/expressions.py +0 -0
  64. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/extended.py +0 -0
  65. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/fields.py +0 -0
  66. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/introspection.py +0 -0
  67. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/listen.py +0 -0
  68. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/loop_filters.py +0 -0
  69. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/model_dto.py +0 -0
  70. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/model_view.py +0 -0
  71. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/one_database.py +0 -0
  72. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/paginator.py +0 -0
  73. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/pool_maintenance.py +0 -0
  74. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/pool_watch.py +0 -0
  75. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/pooler_role.py +0 -0
  76. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/pydantic_bridge.py +0 -0
  77. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/registry.py +0 -0
  78. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/relations.py +0 -0
  79. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/resilience.py +0 -0
  80. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/rls_session.py +0 -0
  81. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/session_context.py +0 -0
  82. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/signals.py +0 -0
  83. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/tls.py +0 -0
  84. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/transaction.py +0 -0
  85. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/types.py +0 -0
  86. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/core/write_queue.py +0 -0
  87. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/entity.py +0 -0
  88. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/error_handling.py +0 -0
  89. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/exceptions.py +0 -0
  90. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/extended/__init__.py +0 -0
  91. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/extended/app_error_handler.py +0 -0
  92. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/identity_rule.py +0 -0
  93. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/local_sqlite.py +0 -0
  94. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/middleware/__init__.py +0 -0
  95. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/middleware/base.py +0 -0
  96. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/MODULE_README.md +0 -0
  97. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/__init__.py +0 -0
  98. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/cli.py +0 -0
  99. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/ddl.py +0 -0
  100. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/diff.py +0 -0
  101. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/executor.py +0 -0
  102. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/integration.py +0 -0
  103. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/loader.py +0 -0
  104. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/operations.py +0 -0
  105. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/state.py +0 -0
  106. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/migrations/table_filter.py +0 -0
  107. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/no_org_assignment.py +0 -0
  108. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/FEATURE.md +0 -0
  109. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/MODULE_README.md +0 -0
  110. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/__init__.py +0 -0
  111. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/bulk_update_values.py +0 -0
  112. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/catalog_select.py +0 -0
  113. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/conflict.py +0 -0
  114. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/conflict_writes.py +0 -0
  115. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/create.py +0 -0
  116. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/db_functions.py +0 -0
  117. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/delete.py +0 -0
  118. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/dynamic_admin.py +0 -0
  119. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/dynamic_crud.py +0 -0
  120. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/graph_walk.py +0 -0
  121. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/latest_rows.py +0 -0
  122. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/queue_claim.py +0 -0
  123. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/read.py +0 -0
  124. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/relational_backfill.py +0 -0
  125. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/staging_load.py +0 -0
  126. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/operations/update.py +0 -0
  127. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/platform_access.py +0 -0
  128. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/platform_db.py +0 -0
  129. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/py.typed +0 -0
  130. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/pytest_plugin.py +0 -0
  131. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/python_sql/MODULE_README.md +0 -0
  132. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/python_sql/__init__.py +0 -0
  133. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/python_sql/db_objects.py +0 -0
  134. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/python_sql/table_detailed_relationships.py +0 -0
  135. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/python_sql/table_typescript_relationship.py +0 -0
  136. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/query/FEATURE.md +0 -0
  137. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/query/MODULE_README.md +0 -0
  138. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/query/__init__.py +0 -0
  139. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/query/builder.py +0 -0
  140. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/query/executor.py +0 -0
  141. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/resources/__init__.py +0 -0
  142. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/resources/additional-assigners-2026-09-12.json +0 -0
  143. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/resources/org_assignment_trigger_baseline.json +0 -0
  144. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/FEATURE.md +0 -0
  145. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/MODULE_README.md +0 -0
  146. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/__init__.py +0 -0
  147. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/code_handler.py +0 -0
  148. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/codegen_writer.py +0 -0
  149. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/columns.py +0 -0
  150. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/common.py +0 -0
  151. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/diff_preview.py +0 -0
  152. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/entity_capabilities.py +0 -0
  153. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/generator.py +0 -0
  154. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/helpers/__init__.py +0 -0
  155. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/helpers/base_generators.py +0 -0
  156. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/helpers/entity_generators.py +0 -0
  157. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/helpers/git_checker.py +0 -0
  158. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/package_wiring.py +0 -0
  159. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/relationships.py +0 -0
  160. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/runner.py +0 -0
  161. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/schema.py +0 -0
  162. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/schema_manager.py +0 -0
  163. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/tables.py +0 -0
  164. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/schema_builder/views.py +0 -0
  165. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/attachments.py +0 -0
  166. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/crypto.py +0 -0
  167. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/host_authorization.py +0 -0
  168. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/secrets_battery/mutation_receipts.py +0 -0
  169. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/README.md +0 -0
  170. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/__init__.py +0 -0
  171. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/coalesce.py +0 -0
  172. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/dag.py +0 -0
  173. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/errors.py +0 -0
  174. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/fallback.py +0 -0
  175. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/flush.py +0 -0
  176. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/lifecycle.py +0 -0
  177. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/managed.py +0 -0
  178. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/op.py +0 -0
  179. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/reads.py +0 -0
  180. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/session.py +0 -0
  181. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/session/telemetry.py +0 -0
  182. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/sql_executor/MODULE_README.md +0 -0
  183. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/sql_executor/__init__.py +0 -0
  184. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/sql_executor/executor.py +0 -0
  185. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/sql_executor/queries.py +0 -0
  186. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/sql_executor/registry.py +0 -0
  187. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/sql_executor/types.py +0 -0
  188. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/sql_executor/utils.py +0 -0
  189. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/state.py +0 -0
  190. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/utils/__init__.py +0 -0
  191. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/utils/sql_utils.py +0 -0
  192. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/matrx_orm/utils/type_converters.py +0 -0
  193. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/scripts/git-branches.sh +0 -0
  194. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/scripts/publish.sh +0 -0
  195. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/scripts/release.sh +0 -0
  196. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/MODULE_README.md +0 -0
  197. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/__init__.py +0 -0
  198. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/conftest.py +0 -0
  199. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/MODULE_README.md +0 -0
  200. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/__init__.py +0 -0
  201. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_adapter_registry.py +0 -0
  202. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_admin_db_columns.py +0 -0
  203. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_admin_response_rendering.py +0 -0
  204. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_admin_search_negation.py +0 -0
  205. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_admin_search_sentinels.py +0 -0
  206. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_advisory_lock.py +0 -0
  207. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_agent_message.py +0 -0
  208. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_api_auth.py +0 -0
  209. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_api_config.py +0 -0
  210. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_api_handlers.py +0 -0
  211. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_api_protocol.py +0 -0
  212. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_array_agg_order_by.py +0 -0
  213. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_associations.py +0 -0
  214. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_associations_wiring.py +0 -0
  215. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_bulk_hydration_offload.py +0 -0
  216. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_bulk_update_by_pk.py +0 -0
  217. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_bulk_upsert_increment_set_fields.py +0 -0
  218. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_cache_debug.py +0 -0
  219. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_call_function.py +0 -0
  220. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_case_when.py +0 -0
  221. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_catalog.py +0 -0
  222. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_composite_pk_filter.py +0 -0
  223. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_composite_pk_write_paths.py +0 -0
  224. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_config.py +0 -0
  225. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_conflict_target.py +0 -0
  226. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_conflict_writes.py +0 -0
  227. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_connection_codecs.py +0 -0
  228. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_connection_poison_guard.py +0 -0
  229. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_connection_primitives.py +0 -0
  230. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_count_composite_distinct.py +0 -0
  231. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_count_limit_param_leak.py +0 -0
  232. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_db_function_field.py +0 -0
  233. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_ddl_generator.py +0 -0
  234. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_derived_table.py +0 -0
  235. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_dirty_tracking.py +0 -0
  236. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_dynamic_crud.py +0 -0
  237. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_exceptions.py +0 -0
  238. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_execute_query_integrity_mapping.py +0 -0
  239. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_expression_primitives.py +0 -0
  240. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_fields.py +0 -0
  241. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_filter_jsonb_agg_array_index.py +0 -0
  242. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_from_alias.py +0 -0
  243. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_fts.py +0 -0
  244. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_func_expression_args.py +0 -0
  245. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_graph_walk.py +0 -0
  246. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_having_annotate_alias.py +0 -0
  247. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_insert_ignore_and_admin_primitives.py +0 -0
  248. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_introspect_rls_policies.py +0 -0
  249. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_join_and_aggregate.py +0 -0
  250. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_latest_rows.py +0 -0
  251. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_listen_notify.py +0 -0
  252. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_local_sqlite.py +0 -0
  253. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_lock_contention_retry.py +0 -0
  254. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_managed_write_guard.py +0 -0
  255. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_matrx_entity.py +0 -0
  256. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_migration_diff_types.py +0 -0
  257. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_migration_loader.py +0 -0
  258. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_model_instance.py +0 -0
  259. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_model_meta.py +0 -0
  260. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_optimistic_concurrency.py +0 -0
  261. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_order_by_expression.py +0 -0
  262. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_org_assignment_inventory.py +0 -0
  263. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_parameter_name_hydration.py +0 -0
  264. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_patch_jsonb_path.py +0 -0
  265. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_persisted_count.py +0 -0
  266. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_pool_holder_census.py +0 -0
  267. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_pool_loop_recreation.py +0 -0
  268. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_pool_maintenance.py +0 -0
  269. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_postgrest_filters.py +0 -0
  270. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_pydantic_bridge.py +0 -0
  271. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_query_builder.py +0 -0
  272. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_query_error_detail.py +0 -0
  273. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_query_executor_sql.py +0 -0
  274. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_query_timeout.py +0 -0
  275. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_querybuilder_clone_semantics.py +0 -0
  276. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_queue_claim.py +0 -0
  277. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_read_connection_retry.py +0 -0
  278. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_registry.py +0 -0
  279. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_registry_multi_database.py +0 -0
  280. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_registry_multi_schema.py +0 -0
  281. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_relational_backfill.py +0 -0
  282. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_relations.py +0 -0
  283. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_resilience.py +0 -0
  284. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_rls_session_scope.py +0 -0
  285. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_schema_exists.py +0 -0
  286. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_session_advisory_lock.py +0 -0
  287. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_state_cache.py +0 -0
  288. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_subquery_filter_raw.py +0 -0
  289. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_subquery_in_filter.py +0 -0
  290. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_subquery_nested_expressions.py +0 -0
  291. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_supabase_auth.py +0 -0
  292. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_supabase_config.py +0 -0
  293. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_tls.py +0 -0
  294. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_typed_join_projection.py +0 -0
  295. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_typed_predicates.py +0 -0
  296. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_update_case_expression.py +0 -0
  297. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_update_subquery.py +0 -0
  298. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_update_with_rebase.py +0 -0
  299. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_upsert_default_leak.py +0 -0
  300. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_upsert_with_conflict.py +0 -0
  301. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_values_jsonb_decode.py +0 -0
  302. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_watched_lifecycle.py +0 -0
  303. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_write_queue_deadline.py +0 -0
  304. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level1/test_write_retry_semantics.py +0 -0
  305. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/MODULE_README.md +0 -0
  306. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/__init__.py +0 -0
  307. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/conftest.py +0 -0
  308. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_bulk_ops.py +0 -0
  309. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_cache_integration.py +0 -0
  310. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_crud.py +0 -0
  311. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_foreign_keys.py +0 -0
  312. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_m2m.py +0 -0
  313. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_manager.py +0 -0
  314. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_migrations_live.py +0 -0
  315. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_query_execution.py +0 -0
  316. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/level2/test_schema_diff.py +0 -0
  317. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/live_pooler_role.py +0 -0
  318. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project/.env.example +0 -0
  319. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project/README.md +0 -0
  320. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project/__init__.py +0 -0
  321. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project/generate.py +0 -0
  322. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project/generated/.gitkeep +0 -0
  323. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project/matrx_orm.yaml +0 -0
  324. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project/test_schema_generation.py +0 -0
  325. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project_desktop/.env.example +0 -0
  326. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project_desktop/README.md +0 -0
  327. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project_desktop/__init__.py +0 -0
  328. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project_desktop/client_example.py +0 -0
  329. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project_desktop/client_example.ts +0 -0
  330. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project_desktop/client_supabase_example.py +0 -0
  331. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/sample_project_desktop/server.py +0 -0
  332. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/entity_tests.py +0 -0
  333. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_base_generation.py +0 -0
  334. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_composite_pk_fk_generation.py +0 -0
  335. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_composite_unique_generation.py +0 -0
  336. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_dto_identity_generation.py +0 -0
  337. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_entity_capability_generation.py +0 -0
  338. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_enum_ordering.py +0 -0
  339. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_generate_schema.py +0 -0
  340. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_junction_analysis.py +0 -0
  341. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_multi_schema_output.py +0 -0
  342. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_schema_analysis.py +0 -0
  343. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_sql_expression_defaults.py +0 -0
  344. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_table_relationships_query.py +0 -0
  345. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema/test_view_generation.py +0 -0
  346. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema_builder/test_array_default_parsing.py +0 -0
  347. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema_builder/test_focused_generation_plan.py +0 -0
  348. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/schema_builder/test_host_models_aggregator.py +0 -0
  349. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/__init__.py +0 -0
  350. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/conftest.py +0 -0
  351. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_cache_no_downgrade.py +0 -0
  352. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_capture_poison_proof.py +0 -0
  353. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_coalesce.py +0 -0
  354. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_dag.py +0 -0
  355. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_disk_spill_fallback.py +0 -0
  356. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_flush_individual_fallback.py +0 -0
  357. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_governed_write.py +0 -0
  358. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_managed.py +0 -0
  359. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_op_enqueue_site.py +0 -0
  360. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_reads.py +0 -0
  361. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_record_error_bounded_insert.py +0 -0
  362. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_record_error_storm.py +0 -0
  363. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_session.py +0 -0
  364. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/session/test_spill_guard_plugin.py +0 -0
  365. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_array_arg_element_type.py +0 -0
  366. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_database_name_alias.py +0 -0
  367. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_db_functions.py +0 -0
  368. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_dynamic_select_unregistered.py +0 -0
  369. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_keyed_digest.py +0 -0
  370. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_model_cls_refactor.py +0 -0
  371. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_no_blocking_asyncpg_tls.py +0 -0
  372. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_one_database.py +0 -0
  373. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_order_by_desc_nulls_last.py +0 -0
  374. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_pooler_role_guard.py +0 -0
  375. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_sql_param_casts.py +0 -0
  376. {matrx_orm-3.1.140 → matrx_orm-3.1.142}/tests/test_sync_twins_guard.py +0 -0
@@ -52,7 +52,7 @@ Every table that becomes a Model needs row identity: a single-column primary key
52
52
  - **Schema builder** (`matrx_orm.schema_builder`): code generation for Python + TypeScript type definitions from the DB schema. Postgres **VIEWs** are emitted as **read-only `Model` classes** into the same schema models file (on by default; per-entry `output.include_views: false` opts a schema out, `output.exclude_views: [...]` skips individual views). View models get no manager/DTO/base-class/auto_config (views are not writable), synthesize `_primary_keys` from an `id` column (else the first column, for cache-key/`load_by_id` only), and emit plain typed fields with **no `ForeignKey`** (views carry no FK metadata — nothing cross-schema to resolve). Never hand-write a view model again.
53
53
  - **Secrets battery** (`matrx_orm.secrets_battery`): the ONE canonical user-credential vault — `users.user_secrets` CRUD + the platform's single Fernet primitive, keyed by `CREDENTIALS_ENCRYPTION_KEY`. Any package: `configure_secrets(db_config_name=...)` once, then `await require_user_secret(user_id, key)`. No package ever calls another service to resolve a credential. Contract: [`matrx_orm/secrets_battery/FEATURE.md`](matrx_orm/secrets_battery/FEATURE.md).
54
54
  - **Polymorphic associations** (`matrx_orm.associations`): `EntityRegistry` (token→Model) + `Associations` — a registry-driven, **access-gated** edge engine over a host's polymorphic edge table. Every `source→target` hop is org-scoped *and* each resolved endpoint is re-authorized against the ACTOR (fail-closed, screams on a genuine leak), so a hop can't widen access past what the actor may reach. 🚨 **The gate is `access_check` (wire `matrx_orm.platform_access_check` → `iam.has_access_for`), NEVER an org-column comparison** — a person belongs to several orgs on purpose, so org-equality denies her own data across her own workspaces (2026-08-30 incident) while granting any co-member of a private row. The edge's `org_id` is a TENANCY stamp, not a permission. Org-equality survives only for UNATTENDED writes (no actor) as an integrity guard, and **system vocabulary is exempt entirely** (existence still required): a model generated with `_rls_variant="system"` is platform-shared dictionary data (seo.keyword/topic) every tenant legitimately links to. Hardcodes no table names — the host wires its own edge + token-registry models.
55
- - **The host's declared session context** (`matrx_orm.core.session_context`): `configure_session_context(provider)` once at startup, and every **top-level** `transaction()` — including the one `rls_session` opens, before it drops to the restricted role — emits whatever the provider returns as transaction-local GUCs. This is how an application-level declaration reaches Postgres at all: a GUC set outside a transaction has no meaning, so the BEGIN is the only place it can land. aidream wires it to its provenance declaration (`app.actor_tier` / `app.actor_system`, read by `platform.actor_tier()`, the history capture and the tier rule). A nested entry is a savepoint inside a transaction that already carries them and emits nothing — and a write made **outside** any transaction carries no declaration at all, which is why a single-row write that must record its author still opens one. No provider configured ⇒ no GUCs (standalone behaviour unchanged); a provider that raises fails the transaction loudly rather than committing rows with a false provenance.
55
+ - **The host's declared session context** (`matrx_orm.core.session_context`): `configure_session_context(provider)` once at startup, and every **top-level** `transaction()` — including the one `rls_session` opens, before it drops to the restricted role — emits whatever the provider returns as transaction-local GUCs. This is how an application-level declaration reaches Postgres at all: a GUC set outside a transaction has no meaning, so the BEGIN is the only place it can land. aidream wires it to its provenance declaration (`app.actor_tier` / `app.actor_system`, read by `platform.actor_tier()`, the history capture and the tier rule). A nested entry is a savepoint inside a transaction that already carries them and emits nothing. A standalone write (`execute_write` outside any transaction) made while the provider has something to say opens its own transaction for that one statement — and still goes through the write queue, inside a copy of the caller's context, so the queue's concurrency gate is kept and the drain task's foreign context cannot hide the declaration (`tests/level1/test_standalone_write_session_context.py`). No provider configured ⇒ no GUCs (standalone behaviour unchanged); a provider that raises fails the transaction loudly rather than committing rows with a false provenance.
56
56
  - **The platform access policy** (`matrx_orm.platform_access`): `platform_access_check(actor, token, id, level)` → `iam.has_access_for`. The ONE answer to "may this actor touch this row?", shared with RLS and the file-access gate. Fails closed. Never re-derive it from `organization_id` or `created_by` — both are strict subsets that deny legitimate access.
57
57
 
58
58
  Public API is defined explicitly in `matrx_orm/__init__.py` (100+ exports). Treat changes there as semver-breaking.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: matrx-orm
3
- Version: 3.1.140
3
+ Version: 3.1.142
4
4
  Summary: Async-first PostgreSQL ORM with bidirectional migrations, schema introspection, many-to-many relationships, and built-in state caching
5
5
  Project-URL: Homepage, https://github.com/AI-Matrix-Engine/aidream-current
6
6
  Project-URL: Repository, https://github.com/AI-Matrix-Engine/aidream-current
@@ -25,7 +25,7 @@ Requires-Python: >=3.13
25
25
  Requires-Dist: asyncpg>=0.31.0
26
26
  Requires-Dist: cryptography>=43.0
27
27
  Requires-Dist: gitpython>=3.1.40
28
- Requires-Dist: matrx-utils>=2.0.36
28
+ Requires-Dist: matrx-utils>=2.0.38
29
29
  Requires-Dist: psycopg-pool>=3.2.5
30
30
  Requires-Dist: psycopg[binary]>=3.2.5
31
31
  Requires-Dist: python-dotenv>=1.0
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ import contextvars
4
5
  from dataclasses import dataclass
5
6
  import json
6
7
  import logging
@@ -1605,25 +1606,46 @@ class AsyncDatabaseManager:
1605
1606
  except ImportError:
1606
1607
  pass
1607
1608
 
1609
+ from matrx_orm.core.write_queue import get_write_queue_registry
1610
+
1611
+ config_name = resolve_database_name(config_name)
1612
+ wq = get_write_queue_registry().get(config_name)
1613
+
1608
1614
  # A declared host session context (notably actor provenance) is made of
1609
1615
  # transaction-local GUCs. A plain standalone write used to skip BEGIN,
1610
1616
  # so its trigger saw the database default even though the caller had
1611
- # explicitly declared an actor. Keep the declaration in the caller's
1612
- # task (the write-queue drain has a different ContextVar context) and
1613
- # use the canonical transaction path for this one write.
1617
+ # explicitly declared an actor — so a declared write takes the
1618
+ # canonical transaction path (BEGIN → GUCs → statement → COMMIT).
1619
+ #
1620
+ # It still goes through the write queue. The queue's drain executes
1621
+ # each item in a task forked from the DRAIN's context, where the
1622
+ # caller's ContextVar declaration is invisible; so the item is run
1623
+ # inside a copy of the caller's context instead of skipping the
1624
+ # queue. Skipping was the first fix (2026-09-12) and it was fine while
1625
+ # only a few doors declared; once the request boundary declares for
1626
+ # EVERY authenticated request, skipping would have moved every
1627
+ # request write off the queue's concurrency gate and onto raw pool
1628
+ # acquires — the pool-storm class the queue exists to prevent.
1614
1629
  from matrx_orm.core.session_context import session_context_gucs
1615
1630
 
1616
1631
  if session_context_gucs():
1617
1632
  from matrx_orm.core.transaction import transaction
1618
1633
 
1619
- config_name = resolve_database_name(config_name)
1620
- async with transaction(config_name):
1621
- return await cls.execute_query(config_name, query, *args, timeout=timeout)
1634
+ async def _declared_write() -> list[dict[str, Any]]:
1635
+ async with transaction(config_name):
1636
+ return await cls.execute_query(config_name, query, *args, timeout=timeout)
1622
1637
 
1623
- from matrx_orm.core.write_queue import get_write_queue_registry
1638
+ if wq is None:
1639
+ return await _declared_write()
1624
1640
 
1625
- config_name = resolve_database_name(config_name)
1626
- wq = get_write_queue_registry().get(config_name)
1641
+ caller_context = contextvars.copy_context()
1642
+
1643
+ async def _in_caller_context() -> list[dict[str, Any]]:
1644
+ # Awaited by the drain's item task; the inner task carries the
1645
+ # caller's context so the session provider sees the declaration.
1646
+ return await asyncio.create_task(_declared_write(), context=caller_context)
1647
+
1648
+ return await wq.submit(_in_caller_context)
1627
1649
 
1628
1650
  if wq is not None:
1629
1651
  return await wq.submit(
@@ -47,6 +47,16 @@ artifacts, not general document storage.
47
47
  `editable=false` blocks human value changes (`FieldNotEditableError`); the owning
48
48
  service rotates via `system_rotation=True` / `rotate_credential_fields`.
49
49
 
50
+ **Protected execution purpose.** `execution_purpose` is a closed, immutable
51
+ row purpose. `passkey_private` is reserved for native-provider use and is
52
+ never creatable through generic battery or HTTP constructors. Generic
53
+ single-field resolution refuses it before decrypt with the stable
54
+ `protected_execution_required` code; bulk env/token resolution omits it.
55
+ Item metadata and grants remain metadata-only, while generic child mutation,
56
+ fork, and transfer refuse a protected child. Every competing child/lifecycle
57
+ mutation locks and re-reads the parent in its transaction; whole-item delete
58
+ remains atomic.
59
+
50
60
  **`inject_into_sandbox=true` REQUIRES an env alias — refused otherwise.** A
51
61
  container environment is a NAME→value map, so a field with `key IS NULL` has
52
62
  nowhere to land and bulk resolution drops it. Accepting the flag anyway meant a
@@ -76,6 +76,7 @@ from .items import (
76
76
  CredentialItemNotFoundError,
77
77
  CredentialItemRow,
78
78
  FieldNotEditableError,
79
+ ProtectedSecretPurposeError,
79
80
  FieldSpec,
80
81
  ItemGrantSpec,
81
82
  SandboxInjectionWithoutEnvKeyError,
@@ -210,6 +211,7 @@ __all__ = [
210
211
  "CredentialItemNotFoundError",
211
212
  "CredentialItemRow",
212
213
  "FieldNotEditableError",
214
+ "ProtectedSecretPurposeError",
213
215
  "FieldSpec",
214
216
  "ItemGrantSpec",
215
217
  "SandboxInjectionWithoutEnvKeyError",
@@ -67,6 +67,9 @@ def register_non_transferable_field_key(field_key: str) -> None:
67
67
 
68
68
 
69
69
  HANDLING_MODES: tuple[str, ...] = ("visible", "revealable", "sealed")
70
+ GENERAL_EXECUTION_PURPOSE = "general"
71
+ PASSKEY_PRIVATE_EXECUTION_PURPOSE = "passkey_private"
72
+ PROTECTED_FIELD_KEY = "passkey_private"
70
73
  ITEM_STATUSES: tuple[str, ...] = ("active", "disabled", "expired", "pending_verification")
71
74
  URI_MATCH_MODES: tuple[str, ...] = ("host", "domain", "exact", "never")
72
75
  ITEM_SOURCES: tuple[str, ...] = (
@@ -107,6 +110,24 @@ class SandboxInjectionWithoutEnvKeyError(ValueError):
107
110
  together) or leave injection off."""
108
111
 
109
112
 
113
+ class ProtectedSecretPurposeError(PermissionError):
114
+ """A generic path reached a native-provider-only secret before decrypt."""
115
+
116
+ code = "protected_execution_required"
117
+
118
+ def __init__(self, *, field_id: str | None, credential_item_id: str | None) -> None:
119
+ self.field_id = field_id
120
+ self.credential_item_id = credential_item_id
121
+ super().__init__(self.code)
122
+
123
+ def metadata(self) -> dict[str, str | None]:
124
+ return {
125
+ "code": self.code,
126
+ "field_id": self.field_id,
127
+ "credential_item_id": self.credential_item_id,
128
+ }
129
+
130
+
110
131
  # ── Shapes ────────────────────────────────────────────────────────────────
111
132
 
112
133
 
@@ -153,6 +174,7 @@ class CredentialFieldRow:
153
174
  description: str | None
154
175
  created_at: str
155
176
  updated_at: str
177
+ execution_purpose: str
156
178
 
157
179
  def to_dict(self) -> dict[str, Any]:
158
180
  return dict(self.__dict__)
@@ -308,6 +330,7 @@ _FIELD_FIELDS = (
308
330
  "id",
309
331
  "credential_item_id",
310
332
  "field_key",
333
+ "execution_purpose",
311
334
  "key",
312
335
  "handling",
313
336
  "editable",
@@ -354,6 +377,7 @@ def _field_row(raw: dict[str, Any]) -> CredentialFieldRow:
354
377
  id=str(raw["id"]),
355
378
  credential_item_id=str(raw["credential_item_id"]),
356
379
  field_key=str(raw["field_key"] or ""),
380
+ execution_purpose=_execution_purpose(raw),
357
381
  env_key=raw.get("key"),
358
382
  handling=str(raw.get("handling") or "revealable"),
359
383
  editable=bool(raw.get("editable", True)),
@@ -388,6 +412,47 @@ async def _require_item_raw(item_id: str) -> dict[str, Any]:
388
412
  return rows[0]
389
413
 
390
414
 
415
+ async def _lock_item_raw(item_id: str) -> dict[str, Any]:
416
+ """Lock and re-read a live parent within the current transaction."""
417
+ rows = await (
418
+ CredentialItem.filter(id=item_id, deleted_at__isnull=True)
419
+ .select_for_update()
420
+ .values(*_ITEM_FIELDS)
421
+ )
422
+ if not rows:
423
+ raise CredentialItemNotFoundError(f"credential item {item_id} not found")
424
+ return rows[0]
425
+
426
+
427
+ def _execution_purpose(raw: dict[str, Any]) -> str:
428
+ purpose = raw.get("execution_purpose")
429
+ if purpose not in (GENERAL_EXECUTION_PURPOSE, PASSKEY_PRIVATE_EXECUTION_PURPOSE):
430
+ # A missing projection is a security-contract failure, never evidence
431
+ # that the row is ordinary.
432
+ raise ProtectedSecretPurposeError(
433
+ field_id=str(raw["id"]) if raw.get("id") else None,
434
+ credential_item_id=(str(raw["credential_item_id"]) if raw.get("credential_item_id") else None),
435
+ )
436
+ return str(purpose)
437
+
438
+
439
+ def _is_protected(raw: dict[str, Any]) -> bool:
440
+ return _execution_purpose(raw) == PASSKEY_PRIVATE_EXECUTION_PURPOSE
441
+
442
+
443
+ def _refuse_protected(raw: dict[str, Any]) -> None:
444
+ if _is_protected(raw):
445
+ raise ProtectedSecretPurposeError(
446
+ field_id=str(raw["id"]) if raw.get("id") else None,
447
+ credential_item_id=(str(raw["credential_item_id"]) if raw.get("credential_item_id") else None),
448
+ )
449
+
450
+
451
+ def _refuse_reserved_generic_field(field_key: str) -> None:
452
+ if field_key == PROTECTED_FIELD_KEY:
453
+ raise ValueError("field_key 'passkey_private' is reserved for native provider execution")
454
+
455
+
391
456
  # ── Item CRUD ─────────────────────────────────────────────────────────────
392
457
 
393
458
 
@@ -516,6 +581,7 @@ async def create_credential_bundle(
516
581
  seen: set[str] = set()
517
582
  for spec in fields:
518
583
  _validate_field_key(spec.field_key)
584
+ _refuse_reserved_generic_field(spec.field_key)
519
585
  _validate_handling(spec.handling)
520
586
  _validate_sandbox_injectable(
521
587
  field_key=spec.field_key,
@@ -693,9 +759,9 @@ async def delete_credential_item(
693
759
  ) -> None:
694
760
  """Soft-delete the item, all its fields, and drop its grants — atomically.
695
761
  Resolution stops immediately; ciphertext is recoverable until purge."""
696
- raw = await _require_item_raw(item_id)
697
762
  now = datetime.now(UTC)
698
763
  async with transaction(PACKAGE_DB_NAME):
764
+ raw = await _lock_item_raw(item_id)
699
765
  from .attachments import soft_delete_item_attachments
700
766
 
701
767
  await UserSecretGrant.delete_where(credential_item_id=item_id)
@@ -743,6 +809,7 @@ async def add_credential_field(
743
809
  first, then write.
744
810
  """
745
811
  _validate_field_key(spec.field_key)
812
+ _refuse_reserved_generic_field(spec.field_key)
746
813
  _validate_handling(spec.handling)
747
814
  if spec.handling == "sealed":
748
815
  await require_host_authorization(
@@ -758,30 +825,31 @@ async def add_credential_field(
758
825
  )
759
826
  if not isinstance(spec.value, str):
760
827
  raise ValueError("value must be a string")
761
- raw = await _require_item_raw(item_id)
762
- instance = await UserSecret.create(
763
- user_id=raw.get("user_id"),
764
- organization_id=raw.get("organization_id"),
765
- credential_item_id=item_id,
766
- field_key=spec.field_key,
767
- key=spec.env_key,
768
- value_encrypted=encrypt_value(spec.value),
769
- value_hint=mask_value(spec.value),
770
- handling=spec.handling,
771
- editable=spec.editable,
772
- inject_into_sandbox=spec.inject_into_sandbox,
773
- description=spec.description,
774
- access_mode=str(raw.get("access_mode") or "all_members"),
775
- created_by=actor_id,
776
- updated_by=actor_id,
777
- )
778
- await record_secret_audit(
779
- action="field_added",
780
- actor_id=actor_id,
781
- user_secret_id=str(instance.id),
782
- organization_id=audit_organization_id,
783
- metadata={"credential_item_id": item_id, "field_key": spec.field_key},
784
- )
828
+ async with transaction(PACKAGE_DB_NAME):
829
+ raw = await _lock_item_raw(item_id)
830
+ instance = await UserSecret.create(
831
+ user_id=raw.get("user_id"),
832
+ organization_id=raw.get("organization_id"),
833
+ credential_item_id=item_id,
834
+ field_key=spec.field_key,
835
+ key=spec.env_key,
836
+ value_encrypted=encrypt_value(spec.value),
837
+ value_hint=mask_value(spec.value),
838
+ handling=spec.handling,
839
+ editable=spec.editable,
840
+ inject_into_sandbox=spec.inject_into_sandbox,
841
+ description=spec.description,
842
+ access_mode=str(raw.get("access_mode") or "all_members"),
843
+ created_by=actor_id,
844
+ updated_by=actor_id,
845
+ )
846
+ await record_secret_audit(
847
+ action="field_added",
848
+ actor_id=actor_id,
849
+ user_secret_id=str(instance.id),
850
+ organization_id=audit_organization_id,
851
+ metadata={"credential_item_id": item_id, "field_key": spec.field_key},
852
+ )
785
853
  rows = await UserSecret.filter(id=instance.id).values(*_FIELD_FIELDS)
786
854
  return _field_row(rows[0])
787
855
 
@@ -800,22 +868,31 @@ async def update_credential_field_value(
800
868
  token refresh, key rotation)."""
801
869
  if not isinstance(value, str):
802
870
  raise ValueError("value must be a string")
803
- rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True).values(*_FIELD_FIELDS)
871
+ rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True).values("credential_item_id")
804
872
  if not rows:
805
873
  raise SecretNotFoundError(f"credential field {field_id} not found")
806
- raw = rows[0]
807
- if not bool(raw.get("editable", True)) and not system_rotation:
808
- raise FieldNotEditableError(
809
- f"field {raw.get('field_key') or raw.get('key')!r} is not editable — "
810
- "it is managed by its owning integration"
874
+ async with transaction(PACKAGE_DB_NAME):
875
+ if rows[0].get("credential_item_id"):
876
+ await _lock_item_raw(str(rows[0]["credential_item_id"]))
877
+ rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True).select_for_update().values(
878
+ *_FIELD_FIELDS
879
+ )
880
+ if not rows:
881
+ raise SecretNotFoundError(f"credential field {field_id} not found")
882
+ raw = rows[0]
883
+ _refuse_protected(raw)
884
+ if not bool(raw.get("editable", True)) and not system_rotation:
885
+ raise FieldNotEditableError(
886
+ f"field {raw.get('field_key') or raw.get('key')!r} is not editable — "
887
+ "it is managed by its owning integration"
888
+ )
889
+ await UserSecret.update_where(
890
+ {"id": field_id, "deleted_at__isnull": True},
891
+ value_encrypted=encrypt_value(value),
892
+ value_hint=mask_value(value),
893
+ updated_by=actor_id,
894
+ updated_at=datetime.now(UTC),
811
895
  )
812
- await UserSecret.update_where(
813
- {"id": field_id, "deleted_at__isnull": True},
814
- value_encrypted=encrypt_value(value),
815
- value_hint=mask_value(value),
816
- updated_by=actor_id,
817
- updated_at=datetime.now(UTC),
818
- )
819
896
  await record_secret_audit(
820
897
  action="rotated" if system_rotation else "field_updated",
821
898
  actor_id=actor_id,
@@ -852,10 +929,13 @@ async def rotate_credential_fields(
852
929
  async with transaction(PACKAGE_DB_NAME):
853
930
  # Item + field liveness re-checked INSIDE the transaction so a
854
931
  # concurrent delete can't race a half-rotation in.
855
- await _require_item_raw(item_id)
932
+ await _lock_item_raw(item_id)
856
933
  rows = await UserSecret.filter(
857
934
  credential_item_id=item_id, field_key__in=list(new_values), deleted_at__isnull=True
858
- ).values("id", "field_key")
935
+ ).values("id", "field_key", "credential_item_id", "execution_purpose")
936
+ protected = [row for row in rows if _is_protected(row)]
937
+ if protected:
938
+ _refuse_protected(protected[0])
859
939
  found = {str(r["field_key"]): str(r["id"]) for r in rows}
860
940
  missing = sorted(set(new_values) - set(found))
861
941
  if missing:
@@ -916,12 +996,18 @@ async def update_field_metadata(
916
996
  raise ValueError(f"env_key must match {VALID_KEY_RE}; got {env_key!r}")
917
997
 
918
998
  async with transaction(PACKAGE_DB_NAME):
919
- rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True).values(
999
+ rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True).values("credential_item_id")
1000
+ if not rows:
1001
+ raise SecretNotFoundError(f"credential field {field_id} not found")
1002
+ if rows[0].get("credential_item_id"):
1003
+ await _lock_item_raw(str(rows[0]["credential_item_id"]))
1004
+ rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True).select_for_update().values(
920
1005
  *_FIELD_FIELDS, "user_id", "organization_id"
921
1006
  )
922
1007
  if not rows:
923
1008
  raise SecretNotFoundError(f"credential field {field_id} not found")
924
1009
  raw = rows[0]
1010
+ _refuse_protected(raw)
925
1011
 
926
1012
  if handling is not None and str(raw.get("handling")) == "sealed" and handling != "sealed":
927
1013
  raise SealedFieldError(
@@ -1000,13 +1086,26 @@ async def delete_credential_field(
1000
1086
  actor_id: str | None = None,
1001
1087
  ) -> bool:
1002
1088
  """Soft-delete one field. Returns True when a row was affected."""
1003
- result = await UserSecret.update_where(
1004
- {"id": field_id, "deleted_at__isnull": True},
1005
- deleted_at=datetime.now(UTC),
1006
- is_active=False,
1007
- updated_by=actor_id,
1008
- updated_at=datetime.now(UTC),
1009
- )
1089
+ rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True).values("credential_item_id")
1090
+ if not rows:
1091
+ return False
1092
+ async with transaction(PACKAGE_DB_NAME):
1093
+ if rows[0].get("credential_item_id"):
1094
+ await _lock_item_raw(str(rows[0]["credential_item_id"]))
1095
+ rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True).select_for_update().values(
1096
+ "id", "credential_item_id", "execution_purpose"
1097
+ )
1098
+ if not rows:
1099
+ return False
1100
+ raw = rows[0]
1101
+ _refuse_protected(raw)
1102
+ result = await UserSecret.update_where(
1103
+ {"id": field_id, "deleted_at__isnull": True},
1104
+ deleted_at=datetime.now(UTC),
1105
+ is_active=False,
1106
+ updated_by=actor_id,
1107
+ updated_at=datetime.now(UTC),
1108
+ )
1010
1109
  if result.rows_affected:
1011
1110
  await record_secret_audit(
1012
1111
  action="field_deleted",
@@ -1025,10 +1124,11 @@ async def resolve_field_by_id(field_id: str) -> str:
1025
1124
  ``sealed`` fields; never expose through a human-facing surface. The host
1026
1125
  must have authorized the consumer before calling."""
1027
1126
  rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True, is_active=True).values(
1028
- "id", "field_key", "key", "value_encrypted"
1127
+ "id", "credential_item_id", "field_key", "key", "execution_purpose", "value_encrypted"
1029
1128
  )
1030
1129
  if not rows:
1031
1130
  raise SecretNotFoundError(f"credential field {field_id} not found")
1131
+ _refuse_protected(rows[0])
1032
1132
  try:
1033
1133
  value = decrypt_value(rows[0]["value_encrypted"])
1034
1134
  except Exception as exc:
@@ -1048,9 +1148,10 @@ async def resolve_field(item_id: str, field_key: str) -> str:
1048
1148
  field_key=field_key,
1049
1149
  deleted_at__isnull=True,
1050
1150
  is_active=True,
1051
- ).values("id")
1151
+ ).values("id", "credential_item_id", "execution_purpose")
1052
1152
  if not rows:
1053
1153
  raise SecretNotFoundError(f"item {item_id} has no active field {field_key!r}")
1154
+ _refuse_protected(rows[0])
1054
1155
  return await resolve_field_by_id(str(rows[0]["id"]))
1055
1156
 
1056
1157
 
@@ -1062,6 +1163,7 @@ async def resolve_item_fields(item_id: str, *, sandbox_only: bool = False) -> di
1062
1163
  "credential_item_id": item_id,
1063
1164
  "deleted_at__isnull": True,
1064
1165
  "is_active": True,
1166
+ "execution_purpose": GENERAL_EXECUTION_PURPOSE,
1065
1167
  }
1066
1168
  if sandbox_only:
1067
1169
  filters["inject_into_sandbox"] = True
@@ -1103,14 +1205,19 @@ async def reveal_field(
1103
1205
  field_key=field_key,
1104
1206
  deleted_at__isnull=True,
1105
1207
  is_active=True,
1106
- ).values("id", "field_key", "key", "handling", "value_encrypted", "credential_item_id")
1208
+ ).values(
1209
+ "id", "field_key", "key", "handling", "execution_purpose", "value_encrypted",
1210
+ "credential_item_id",
1211
+ )
1107
1212
  else:
1108
1213
  rows = await UserSecret.filter(id=field_id, deleted_at__isnull=True, is_active=True).values(
1109
- "id", "field_key", "key", "handling", "value_encrypted", "credential_item_id"
1214
+ "id", "field_key", "key", "handling", "execution_purpose", "value_encrypted",
1215
+ "credential_item_id",
1110
1216
  )
1111
1217
  if not rows:
1112
1218
  raise SecretNotFoundError("credential field not found")
1113
1219
  raw = rows[0]
1220
+ _refuse_protected(raw)
1114
1221
  handling = str(raw.get("handling") or "revealable")
1115
1222
  if handling == "sealed":
1116
1223
  await record_secret_audit(
@@ -1463,11 +1570,18 @@ async def transfer_credential_item(
1463
1570
  do NOT travel: they are retired in the same transaction and named in the
1464
1571
  returned :class:`TransferResult`."""
1465
1572
  _validate_owner(to_user_id, to_organization_id)
1466
- raw = await _require_item_raw(item_id)
1467
- from_user_id = str(raw["user_id"]) if raw.get("user_id") else None
1468
- from_organization_id = str(raw["organization_id"]) if raw.get("organization_id") else None
1469
1573
  now = datetime.now(UTC)
1470
1574
  async with transaction(PACKAGE_DB_NAME):
1575
+ raw = await _lock_item_raw(item_id)
1576
+ protected = await UserSecret.filter(
1577
+ credential_item_id=item_id,
1578
+ deleted_at__isnull=True,
1579
+ execution_purpose=PASSKEY_PRIVATE_EXECUTION_PURPOSE,
1580
+ ).values("id", "credential_item_id", "execution_purpose")
1581
+ if protected:
1582
+ _refuse_protected(protected[0])
1583
+ from_user_id = str(raw["user_id"]) if raw.get("user_id") else None
1584
+ from_organization_id = str(raw["organization_id"]) if raw.get("organization_id") else None
1471
1585
  retired = await _retire_non_transferable_fields(
1472
1586
  item_id, actor_id=actor_id, now=now, event="transfer"
1473
1587
  )
@@ -1534,29 +1648,22 @@ async def fork_credential_item(
1534
1648
  hands the copy to a principal who never established them (D-15). The new
1535
1649
  item's ``lifecycle.fields_not_copied`` names what was left behind."""
1536
1650
  _validate_owner(to_user_id, to_organization_id)
1537
- raw = await _require_item_raw(item_id)
1538
- all_field_rows = await UserSecret.filter(
1539
- credential_item_id=item_id, deleted_at__isnull=True
1540
- ).values(*_FIELD_FIELDS, "value_encrypted")
1541
- field_rows = [
1542
- fr for fr in all_field_rows if str(fr.get("field_key")) not in NON_TRANSFERABLE_FIELD_KEYS
1543
- ]
1544
- not_copied = sorted(
1545
- {
1651
+ async with transaction(PACKAGE_DB_NAME):
1652
+ raw = await _lock_item_raw(item_id)
1653
+ all_field_rows = await UserSecret.filter(
1654
+ credential_item_id=item_id, deleted_at__isnull=True
1655
+ ).values(*_FIELD_FIELDS, "value_encrypted")
1656
+ protected = [row for row in all_field_rows if _is_protected(row)]
1657
+ if protected:
1658
+ _refuse_protected(protected[0])
1659
+ field_rows = [
1660
+ fr for fr in all_field_rows if str(fr.get("field_key")) not in NON_TRANSFERABLE_FIELD_KEYS
1661
+ ]
1662
+ not_copied = sorted(
1546
1663
  str(fr.get("field_key"))
1547
1664
  for fr in all_field_rows
1548
1665
  if str(fr.get("field_key")) in NON_TRANSFERABLE_FIELD_KEYS
1549
- }
1550
- )
1551
- if not_copied:
1552
- logger.warning(
1553
- "credential item %s: fork does NOT copy minting field(s) %s — the fork's "
1554
- "owner must establish them again (D-15)",
1555
- item_id,
1556
- not_copied,
1557
1666
  )
1558
-
1559
- async with transaction(PACKAGE_DB_NAME):
1560
1667
  new_item = await CredentialItem.create(
1561
1668
  user_id=to_user_id,
1562
1669
  organization_id=to_organization_id,
@@ -80,6 +80,7 @@ class UserSecret(Model):
80
80
  source_user_secret_version = IntegerField()
81
81
  credential_item_id = UUIDField()
82
82
  field_key = TextField()
83
+ execution_purpose = TextField(null=False, default="general")
83
84
  handling = TextField(null=False, default="revealable")
84
85
  editable = BooleanField(null=False, default=True)
85
86
  key = TextField()